mirror of https://github.com/aya-rs/aya
Initial commit
commit
15be301f8c
@ -0,0 +1,2 @@
|
|||||||
|
Cargo.lock
|
||||||
|
target/
|
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "aya"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Alessandro Decina <alessandro.d@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
thiserror = "1"
|
||||||
|
object = "0.23"
|
||||||
|
bytes = "1"
|
||||||
|
lazy_static = "1"
|
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
LIBBPF_DIR=$1
|
||||||
|
OUTPUT_DIR=$2
|
||||||
|
|
||||||
|
if test -z "$LIBBPF_DIR"; then
|
||||||
|
echo "error: no libbpf dir provided"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$OUTPUT_DIR"; then
|
||||||
|
echo "error: no output dir provided"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BPF_TYPES="\
|
||||||
|
bpf_cmd \
|
||||||
|
bpf_insn \
|
||||||
|
bpf_attr \
|
||||||
|
bpf_map_type \
|
||||||
|
bpf_prog_type \
|
||||||
|
bpf_attach_type
|
||||||
|
"
|
||||||
|
|
||||||
|
BPF_VARS="\
|
||||||
|
BPF_PSEUDO_.*
|
||||||
|
"
|
||||||
|
|
||||||
|
PERF_TYPES="\
|
||||||
|
perf_event_attr \
|
||||||
|
perf_sw_ids \
|
||||||
|
perf_event_sample_format \
|
||||||
|
perf_event_mmap_page \
|
||||||
|
perf_event_header \
|
||||||
|
perf_type_id \
|
||||||
|
perf_event_type
|
||||||
|
"
|
||||||
|
|
||||||
|
PERF_VARS="\
|
||||||
|
PERF_FLAG_.* \
|
||||||
|
PERF_EVENT_.*
|
||||||
|
"
|
||||||
|
|
||||||
|
bindgen $LIBBPF_DIR/include/uapi/linux/bpf.h \
|
||||||
|
--no-layout-tests \
|
||||||
|
--default-enum-style moduleconsts \
|
||||||
|
$(for ty in $BPF_TYPES; do
|
||||||
|
echo --whitelist-type "$ty"
|
||||||
|
done) \
|
||||||
|
$(for var in $BPF_VARS; do
|
||||||
|
echo --whitelist-var "$var"
|
||||||
|
done) \
|
||||||
|
> $OUTPUT_DIR/bpf_bindings.rs
|
||||||
|
|
||||||
|
bindgen include/perf_wrapper.h \
|
||||||
|
--no-layout-tests \
|
||||||
|
--default-enum-style moduleconsts \
|
||||||
|
$(for ty in $PERF_TYPES; do
|
||||||
|
echo --whitelist-type "$ty"
|
||||||
|
done) \
|
||||||
|
$(for var in $PERF_VARS; do
|
||||||
|
echo --whitelist-var "$var"
|
||||||
|
done) \
|
||||||
|
> $OUTPUT_DIR/perf_bindings.rs
|
@ -0,0 +1,187 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
generated::bpf_insn,
|
||||||
|
maps::{Map, MapError},
|
||||||
|
obj::{relocate, Object, ParseError, RelocationError},
|
||||||
|
programs::{KProbe, Program, ProgramData, ProgramError, SocketFilter, TracePoint, UProbe, Xdp},
|
||||||
|
syscalls::bpf_map_update_elem_ptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use object::Pod;
|
||||||
|
|
||||||
|
unsafe impl object::Pod for bpf_insn {}
|
||||||
|
|
||||||
|
pub(crate) const BPF_OBJ_NAME_LEN: usize = 16;
|
||||||
|
|
||||||
|
/* FIXME: these are arch dependent */
|
||||||
|
pub(crate) const PERF_EVENT_IOC_ENABLE: libc::c_ulong = 9216;
|
||||||
|
pub(crate) const PERF_EVENT_IOC_DISABLE: libc::c_ulong = 9217;
|
||||||
|
pub(crate) const PERF_EVENT_IOC_SET_BPF: libc::c_ulong = 1074013192;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub(crate) struct bpf_map_def {
|
||||||
|
pub(crate) map_type: u32,
|
||||||
|
pub(crate) key_size: u32,
|
||||||
|
pub(crate) value_size: u32,
|
||||||
|
pub(crate) max_entries: u32,
|
||||||
|
pub(crate) map_flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl object::Pod for bpf_map_def {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Bpf {
|
||||||
|
maps: HashMap<String, Map>,
|
||||||
|
programs: HashMap<String, Program>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bpf {
|
||||||
|
pub fn load(data: &[u8]) -> Result<Bpf, BpfError> {
|
||||||
|
let mut obj = Object::parse(data)?;
|
||||||
|
|
||||||
|
let mut maps = Vec::new();
|
||||||
|
for (_, obj) in obj.maps.drain() {
|
||||||
|
let mut map = Map { obj, fd: None };
|
||||||
|
let fd = map.create()?;
|
||||||
|
if !map.obj.data.is_empty() && map.obj.name != ".bss" {
|
||||||
|
bpf_map_update_elem_ptr(fd, &0 as *const _, map.obj.data.as_ptr(), 0)
|
||||||
|
.map_err(|(code, io_error)| MapError::UpdateElementFailed { code, io_error })?;
|
||||||
|
}
|
||||||
|
maps.push(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
relocate(&mut obj, maps.as_slice())?;
|
||||||
|
|
||||||
|
let programs = obj
|
||||||
|
.programs
|
||||||
|
.drain()
|
||||||
|
.map(|(name, obj)| {
|
||||||
|
let kind = obj.kind;
|
||||||
|
let data = ProgramData {
|
||||||
|
obj,
|
||||||
|
name: name.clone(),
|
||||||
|
fd: None,
|
||||||
|
links: Vec::new(),
|
||||||
|
};
|
||||||
|
let program = match kind {
|
||||||
|
crate::obj::ProgramKind::KProbe => Program::KProbe(KProbe { data }),
|
||||||
|
crate::obj::ProgramKind::UProbe => Program::UProbe(UProbe { data }),
|
||||||
|
crate::obj::ProgramKind::TracePoint => Program::TracePoint(TracePoint { data }),
|
||||||
|
crate::obj::ProgramKind::Xdp => Program::Xdp(Xdp { data }),
|
||||||
|
};
|
||||||
|
|
||||||
|
(name, program)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Bpf {
|
||||||
|
maps: maps
|
||||||
|
.drain(..)
|
||||||
|
.map(|map| (map.obj.name.clone(), map))
|
||||||
|
.collect(),
|
||||||
|
programs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map(&self, name: &str) -> Option<&Map> {
|
||||||
|
self.maps.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_mut(&mut self, name: &str) -> Option<&mut Map> {
|
||||||
|
self.maps.get_mut(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program(&self, name: &str) -> Option<&Program> {
|
||||||
|
self.programs.get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn program_mut(&mut self, name: &str) -> Option<&mut Program> {
|
||||||
|
self.programs.get_mut(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kprobe(&self, name: &str) -> Option<&KProbe> {
|
||||||
|
match self.programs.get(name) {
|
||||||
|
Some(Program::KProbe(kprobe)) => Some(kprobe),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kprobe_mut(&mut self, name: &str) -> Option<&mut KProbe> {
|
||||||
|
match self.programs.get_mut(name) {
|
||||||
|
Some(Program::KProbe(kprobe)) => Some(kprobe),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uprobe(&self, name: &str) -> Option<&UProbe> {
|
||||||
|
match self.programs.get(name) {
|
||||||
|
Some(Program::UProbe(uprobe)) => Some(uprobe),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uprobe_mut(&mut self, name: &str) -> Option<&mut UProbe> {
|
||||||
|
match self.programs.get_mut(name) {
|
||||||
|
Some(Program::UProbe(uprobe)) => Some(uprobe),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trace_point(&self, name: &str) -> Option<&TracePoint> {
|
||||||
|
match self.programs.get(name) {
|
||||||
|
Some(Program::TracePoint(trace_point)) => Some(trace_point),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trace_point_mut(&mut self, name: &str) -> Option<&mut TracePoint> {
|
||||||
|
match self.programs.get_mut(name) {
|
||||||
|
Some(Program::TracePoint(trace_point)) => Some(trace_point),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn socket_filter(&self, name: &str) -> Option<&SocketFilter> {
|
||||||
|
match self.programs.get(name) {
|
||||||
|
Some(Program::SocketFilter(socket_filter)) => Some(socket_filter),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn socket_filter_mut(&mut self, name: &str) -> Option<&mut SocketFilter> {
|
||||||
|
match self.programs.get_mut(name) {
|
||||||
|
Some(Program::SocketFilter(socket_filter)) => Some(socket_filter),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xdp(&self, name: &str) -> Option<&Xdp> {
|
||||||
|
match self.programs.get(name) {
|
||||||
|
Some(Program::Xdp(xdp)) => Some(xdp),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn xdp_mut(&mut self, name: &str) -> Option<&mut Xdp> {
|
||||||
|
match self.programs.get_mut(name) {
|
||||||
|
Some(Program::Xdp(xdp)) => Some(xdp),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum BpfError {
|
||||||
|
#[error("error parsing BPF object: {0}")]
|
||||||
|
ParseError(#[from] ParseError),
|
||||||
|
#[error("error relocating BPF object: {0}")]
|
||||||
|
RelocationError(#[from] RelocationError),
|
||||||
|
#[error("map error: {0}")]
|
||||||
|
MapError(#[from] MapError),
|
||||||
|
#[error("program error: {0}")]
|
||||||
|
ProgramError(#[from] ProgramError),
|
||||||
|
}
|
@ -0,0 +1,542 @@
|
|||||||
|
/* automatically generated by rust-bindgen 0.55.1 */
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct __BindgenBitfieldUnit<Storage, Align> {
|
||||||
|
storage: Storage,
|
||||||
|
align: [Align; 0],
|
||||||
|
}
|
||||||
|
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align> {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(storage: Storage) -> Self {
|
||||||
|
Self { storage, align: [] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
|
||||||
|
where
|
||||||
|
Storage: AsRef<[u8]> + AsMut<[u8]>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub fn get_bit(&self, index: usize) -> bool {
|
||||||
|
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||||
|
let byte_index = index / 8;
|
||||||
|
let byte = self.storage.as_ref()[byte_index];
|
||||||
|
let bit_index = if cfg!(target_endian = "big") {
|
||||||
|
7 - (index % 8)
|
||||||
|
} else {
|
||||||
|
index % 8
|
||||||
|
};
|
||||||
|
let mask = 1 << bit_index;
|
||||||
|
byte & mask == mask
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_bit(&mut self, index: usize, val: bool) {
|
||||||
|
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||||
|
let byte_index = index / 8;
|
||||||
|
let byte = &mut self.storage.as_mut()[byte_index];
|
||||||
|
let bit_index = if cfg!(target_endian = "big") {
|
||||||
|
7 - (index % 8)
|
||||||
|
} else {
|
||||||
|
index % 8
|
||||||
|
};
|
||||||
|
let mask = 1 << bit_index;
|
||||||
|
if val {
|
||||||
|
*byte |= mask;
|
||||||
|
} else {
|
||||||
|
*byte &= !mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
|
||||||
|
debug_assert!(bit_width <= 64);
|
||||||
|
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||||
|
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||||
|
let mut val = 0;
|
||||||
|
for i in 0..(bit_width as usize) {
|
||||||
|
if self.get_bit(i + bit_offset) {
|
||||||
|
let index = if cfg!(target_endian = "big") {
|
||||||
|
bit_width as usize - 1 - i
|
||||||
|
} else {
|
||||||
|
i
|
||||||
|
};
|
||||||
|
val |= 1 << index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
|
||||||
|
debug_assert!(bit_width <= 64);
|
||||||
|
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||||
|
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||||
|
for i in 0..(bit_width as usize) {
|
||||||
|
let mask = 1 << i;
|
||||||
|
let val_bit_is_set = val & mask == mask;
|
||||||
|
let index = if cfg!(target_endian = "big") {
|
||||||
|
bit_width as usize - 1 - i
|
||||||
|
} else {
|
||||||
|
i
|
||||||
|
};
|
||||||
|
self.set_bit(index + bit_offset, val_bit_is_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const BPF_PSEUDO_MAP_FD: u32 = 1;
|
||||||
|
pub const BPF_PSEUDO_MAP_VALUE: u32 = 2;
|
||||||
|
pub const BPF_PSEUDO_BTF_ID: u32 = 3;
|
||||||
|
pub const BPF_PSEUDO_CALL: u32 = 1;
|
||||||
|
pub type __u8 = ::std::os::raw::c_uchar;
|
||||||
|
pub type __s16 = ::std::os::raw::c_short;
|
||||||
|
pub type __s32 = ::std::os::raw::c_int;
|
||||||
|
pub type __u32 = ::std::os::raw::c_uint;
|
||||||
|
pub type __u64 = ::std::os::raw::c_ulonglong;
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_insn {
|
||||||
|
pub code: __u8,
|
||||||
|
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize], u8>,
|
||||||
|
pub off: __s16,
|
||||||
|
pub imm: __s32,
|
||||||
|
}
|
||||||
|
impl bpf_insn {
|
||||||
|
#[inline]
|
||||||
|
pub fn dst_reg(&self) -> __u8 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 4u8) as u8) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_dst_reg(&mut self, val: __u8) {
|
||||||
|
unsafe {
|
||||||
|
let val: u8 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(0usize, 4u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn src_reg(&self) -> __u8 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 4u8) as u8) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_src_reg(&mut self, val: __u8) {
|
||||||
|
unsafe {
|
||||||
|
let val: u8 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(4usize, 4u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn new_bitfield_1(dst_reg: __u8, src_reg: __u8) -> __BindgenBitfieldUnit<[u8; 1usize], u8> {
|
||||||
|
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize], u8> =
|
||||||
|
Default::default();
|
||||||
|
__bindgen_bitfield_unit.set(0usize, 4u8, {
|
||||||
|
let dst_reg: u8 = unsafe { ::std::mem::transmute(dst_reg) };
|
||||||
|
dst_reg as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(4usize, 4u8, {
|
||||||
|
let src_reg: u8 = unsafe { ::std::mem::transmute(src_reg) };
|
||||||
|
src_reg as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub mod bpf_cmd {
|
||||||
|
pub type Type = ::std::os::raw::c_uint;
|
||||||
|
pub const BPF_MAP_CREATE: Type = 0;
|
||||||
|
pub const BPF_MAP_LOOKUP_ELEM: Type = 1;
|
||||||
|
pub const BPF_MAP_UPDATE_ELEM: Type = 2;
|
||||||
|
pub const BPF_MAP_DELETE_ELEM: Type = 3;
|
||||||
|
pub const BPF_MAP_GET_NEXT_KEY: Type = 4;
|
||||||
|
pub const BPF_PROG_LOAD: Type = 5;
|
||||||
|
pub const BPF_OBJ_PIN: Type = 6;
|
||||||
|
pub const BPF_OBJ_GET: Type = 7;
|
||||||
|
pub const BPF_PROG_ATTACH: Type = 8;
|
||||||
|
pub const BPF_PROG_DETACH: Type = 9;
|
||||||
|
pub const BPF_PROG_TEST_RUN: Type = 10;
|
||||||
|
pub const BPF_PROG_GET_NEXT_ID: Type = 11;
|
||||||
|
pub const BPF_MAP_GET_NEXT_ID: Type = 12;
|
||||||
|
pub const BPF_PROG_GET_FD_BY_ID: Type = 13;
|
||||||
|
pub const BPF_MAP_GET_FD_BY_ID: Type = 14;
|
||||||
|
pub const BPF_OBJ_GET_INFO_BY_FD: Type = 15;
|
||||||
|
pub const BPF_PROG_QUERY: Type = 16;
|
||||||
|
pub const BPF_RAW_TRACEPOINT_OPEN: Type = 17;
|
||||||
|
pub const BPF_BTF_LOAD: Type = 18;
|
||||||
|
pub const BPF_BTF_GET_FD_BY_ID: Type = 19;
|
||||||
|
pub const BPF_TASK_FD_QUERY: Type = 20;
|
||||||
|
pub const BPF_MAP_LOOKUP_AND_DELETE_ELEM: Type = 21;
|
||||||
|
pub const BPF_MAP_FREEZE: Type = 22;
|
||||||
|
pub const BPF_BTF_GET_NEXT_ID: Type = 23;
|
||||||
|
pub const BPF_MAP_LOOKUP_BATCH: Type = 24;
|
||||||
|
pub const BPF_MAP_LOOKUP_AND_DELETE_BATCH: Type = 25;
|
||||||
|
pub const BPF_MAP_UPDATE_BATCH: Type = 26;
|
||||||
|
pub const BPF_MAP_DELETE_BATCH: Type = 27;
|
||||||
|
pub const BPF_LINK_CREATE: Type = 28;
|
||||||
|
pub const BPF_LINK_UPDATE: Type = 29;
|
||||||
|
pub const BPF_LINK_GET_FD_BY_ID: Type = 30;
|
||||||
|
pub const BPF_LINK_GET_NEXT_ID: Type = 31;
|
||||||
|
pub const BPF_ENABLE_STATS: Type = 32;
|
||||||
|
pub const BPF_ITER_CREATE: Type = 33;
|
||||||
|
pub const BPF_LINK_DETACH: Type = 34;
|
||||||
|
pub const BPF_PROG_BIND_MAP: Type = 35;
|
||||||
|
}
|
||||||
|
pub mod bpf_map_type {
|
||||||
|
pub type Type = ::std::os::raw::c_uint;
|
||||||
|
pub const BPF_MAP_TYPE_UNSPEC: Type = 0;
|
||||||
|
pub const BPF_MAP_TYPE_HASH: Type = 1;
|
||||||
|
pub const BPF_MAP_TYPE_ARRAY: Type = 2;
|
||||||
|
pub const BPF_MAP_TYPE_PROG_ARRAY: Type = 3;
|
||||||
|
pub const BPF_MAP_TYPE_PERF_EVENT_ARRAY: Type = 4;
|
||||||
|
pub const BPF_MAP_TYPE_PERCPU_HASH: Type = 5;
|
||||||
|
pub const BPF_MAP_TYPE_PERCPU_ARRAY: Type = 6;
|
||||||
|
pub const BPF_MAP_TYPE_STACK_TRACE: Type = 7;
|
||||||
|
pub const BPF_MAP_TYPE_CGROUP_ARRAY: Type = 8;
|
||||||
|
pub const BPF_MAP_TYPE_LRU_HASH: Type = 9;
|
||||||
|
pub const BPF_MAP_TYPE_LRU_PERCPU_HASH: Type = 10;
|
||||||
|
pub const BPF_MAP_TYPE_LPM_TRIE: Type = 11;
|
||||||
|
pub const BPF_MAP_TYPE_ARRAY_OF_MAPS: Type = 12;
|
||||||
|
pub const BPF_MAP_TYPE_HASH_OF_MAPS: Type = 13;
|
||||||
|
pub const BPF_MAP_TYPE_DEVMAP: Type = 14;
|
||||||
|
pub const BPF_MAP_TYPE_SOCKMAP: Type = 15;
|
||||||
|
pub const BPF_MAP_TYPE_CPUMAP: Type = 16;
|
||||||
|
pub const BPF_MAP_TYPE_XSKMAP: Type = 17;
|
||||||
|
pub const BPF_MAP_TYPE_SOCKHASH: Type = 18;
|
||||||
|
pub const BPF_MAP_TYPE_CGROUP_STORAGE: Type = 19;
|
||||||
|
pub const BPF_MAP_TYPE_REUSEPORT_SOCKARRAY: Type = 20;
|
||||||
|
pub const BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: Type = 21;
|
||||||
|
pub const BPF_MAP_TYPE_QUEUE: Type = 22;
|
||||||
|
pub const BPF_MAP_TYPE_STACK: Type = 23;
|
||||||
|
pub const BPF_MAP_TYPE_SK_STORAGE: Type = 24;
|
||||||
|
pub const BPF_MAP_TYPE_DEVMAP_HASH: Type = 25;
|
||||||
|
pub const BPF_MAP_TYPE_STRUCT_OPS: Type = 26;
|
||||||
|
pub const BPF_MAP_TYPE_RINGBUF: Type = 27;
|
||||||
|
pub const BPF_MAP_TYPE_INODE_STORAGE: Type = 28;
|
||||||
|
pub const BPF_MAP_TYPE_TASK_STORAGE: Type = 29;
|
||||||
|
}
|
||||||
|
pub mod bpf_prog_type {
|
||||||
|
pub type Type = ::std::os::raw::c_uint;
|
||||||
|
pub const BPF_PROG_TYPE_UNSPEC: Type = 0;
|
||||||
|
pub const BPF_PROG_TYPE_SOCKET_FILTER: Type = 1;
|
||||||
|
pub const BPF_PROG_TYPE_KPROBE: Type = 2;
|
||||||
|
pub const BPF_PROG_TYPE_SCHED_CLS: Type = 3;
|
||||||
|
pub const BPF_PROG_TYPE_SCHED_ACT: Type = 4;
|
||||||
|
pub const BPF_PROG_TYPE_TRACEPOINT: Type = 5;
|
||||||
|
pub const BPF_PROG_TYPE_XDP: Type = 6;
|
||||||
|
pub const BPF_PROG_TYPE_PERF_EVENT: Type = 7;
|
||||||
|
pub const BPF_PROG_TYPE_CGROUP_SKB: Type = 8;
|
||||||
|
pub const BPF_PROG_TYPE_CGROUP_SOCK: Type = 9;
|
||||||
|
pub const BPF_PROG_TYPE_LWT_IN: Type = 10;
|
||||||
|
pub const BPF_PROG_TYPE_LWT_OUT: Type = 11;
|
||||||
|
pub const BPF_PROG_TYPE_LWT_XMIT: Type = 12;
|
||||||
|
pub const BPF_PROG_TYPE_SOCK_OPS: Type = 13;
|
||||||
|
pub const BPF_PROG_TYPE_SK_SKB: Type = 14;
|
||||||
|
pub const BPF_PROG_TYPE_CGROUP_DEVICE: Type = 15;
|
||||||
|
pub const BPF_PROG_TYPE_SK_MSG: Type = 16;
|
||||||
|
pub const BPF_PROG_TYPE_RAW_TRACEPOINT: Type = 17;
|
||||||
|
pub const BPF_PROG_TYPE_CGROUP_SOCK_ADDR: Type = 18;
|
||||||
|
pub const BPF_PROG_TYPE_LWT_SEG6LOCAL: Type = 19;
|
||||||
|
pub const BPF_PROG_TYPE_LIRC_MODE2: Type = 20;
|
||||||
|
pub const BPF_PROG_TYPE_SK_REUSEPORT: Type = 21;
|
||||||
|
pub const BPF_PROG_TYPE_FLOW_DISSECTOR: Type = 22;
|
||||||
|
pub const BPF_PROG_TYPE_CGROUP_SYSCTL: Type = 23;
|
||||||
|
pub const BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE: Type = 24;
|
||||||
|
pub const BPF_PROG_TYPE_CGROUP_SOCKOPT: Type = 25;
|
||||||
|
pub const BPF_PROG_TYPE_TRACING: Type = 26;
|
||||||
|
pub const BPF_PROG_TYPE_STRUCT_OPS: Type = 27;
|
||||||
|
pub const BPF_PROG_TYPE_EXT: Type = 28;
|
||||||
|
pub const BPF_PROG_TYPE_LSM: Type = 29;
|
||||||
|
pub const BPF_PROG_TYPE_SK_LOOKUP: Type = 30;
|
||||||
|
}
|
||||||
|
pub mod bpf_attach_type {
|
||||||
|
pub type Type = ::std::os::raw::c_uint;
|
||||||
|
pub const BPF_CGROUP_INET_INGRESS: Type = 0;
|
||||||
|
pub const BPF_CGROUP_INET_EGRESS: Type = 1;
|
||||||
|
pub const BPF_CGROUP_INET_SOCK_CREATE: Type = 2;
|
||||||
|
pub const BPF_CGROUP_SOCK_OPS: Type = 3;
|
||||||
|
pub const BPF_SK_SKB_STREAM_PARSER: Type = 4;
|
||||||
|
pub const BPF_SK_SKB_STREAM_VERDICT: Type = 5;
|
||||||
|
pub const BPF_CGROUP_DEVICE: Type = 6;
|
||||||
|
pub const BPF_SK_MSG_VERDICT: Type = 7;
|
||||||
|
pub const BPF_CGROUP_INET4_BIND: Type = 8;
|
||||||
|
pub const BPF_CGROUP_INET6_BIND: Type = 9;
|
||||||
|
pub const BPF_CGROUP_INET4_CONNECT: Type = 10;
|
||||||
|
pub const BPF_CGROUP_INET6_CONNECT: Type = 11;
|
||||||
|
pub const BPF_CGROUP_INET4_POST_BIND: Type = 12;
|
||||||
|
pub const BPF_CGROUP_INET6_POST_BIND: Type = 13;
|
||||||
|
pub const BPF_CGROUP_UDP4_SENDMSG: Type = 14;
|
||||||
|
pub const BPF_CGROUP_UDP6_SENDMSG: Type = 15;
|
||||||
|
pub const BPF_LIRC_MODE2: Type = 16;
|
||||||
|
pub const BPF_FLOW_DISSECTOR: Type = 17;
|
||||||
|
pub const BPF_CGROUP_SYSCTL: Type = 18;
|
||||||
|
pub const BPF_CGROUP_UDP4_RECVMSG: Type = 19;
|
||||||
|
pub const BPF_CGROUP_UDP6_RECVMSG: Type = 20;
|
||||||
|
pub const BPF_CGROUP_GETSOCKOPT: Type = 21;
|
||||||
|
pub const BPF_CGROUP_SETSOCKOPT: Type = 22;
|
||||||
|
pub const BPF_TRACE_RAW_TP: Type = 23;
|
||||||
|
pub const BPF_TRACE_FENTRY: Type = 24;
|
||||||
|
pub const BPF_TRACE_FEXIT: Type = 25;
|
||||||
|
pub const BPF_MODIFY_RETURN: Type = 26;
|
||||||
|
pub const BPF_LSM_MAC: Type = 27;
|
||||||
|
pub const BPF_TRACE_ITER: Type = 28;
|
||||||
|
pub const BPF_CGROUP_INET4_GETPEERNAME: Type = 29;
|
||||||
|
pub const BPF_CGROUP_INET6_GETPEERNAME: Type = 30;
|
||||||
|
pub const BPF_CGROUP_INET4_GETSOCKNAME: Type = 31;
|
||||||
|
pub const BPF_CGROUP_INET6_GETSOCKNAME: Type = 32;
|
||||||
|
pub const BPF_XDP_DEVMAP: Type = 33;
|
||||||
|
pub const BPF_CGROUP_INET_SOCK_RELEASE: Type = 34;
|
||||||
|
pub const BPF_XDP_CPUMAP: Type = 35;
|
||||||
|
pub const BPF_SK_LOOKUP: Type = 36;
|
||||||
|
pub const BPF_XDP: Type = 37;
|
||||||
|
pub const __MAX_BPF_ATTACH_TYPE: Type = 38;
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union bpf_attr {
|
||||||
|
pub __bindgen_anon_1: bpf_attr__bindgen_ty_1,
|
||||||
|
pub __bindgen_anon_2: bpf_attr__bindgen_ty_2,
|
||||||
|
pub batch: bpf_attr__bindgen_ty_3,
|
||||||
|
pub __bindgen_anon_3: bpf_attr__bindgen_ty_4,
|
||||||
|
pub __bindgen_anon_4: bpf_attr__bindgen_ty_5,
|
||||||
|
pub __bindgen_anon_5: bpf_attr__bindgen_ty_6,
|
||||||
|
pub test: bpf_attr__bindgen_ty_7,
|
||||||
|
pub __bindgen_anon_6: bpf_attr__bindgen_ty_8,
|
||||||
|
pub info: bpf_attr__bindgen_ty_9,
|
||||||
|
pub query: bpf_attr__bindgen_ty_10,
|
||||||
|
pub raw_tracepoint: bpf_attr__bindgen_ty_11,
|
||||||
|
pub __bindgen_anon_7: bpf_attr__bindgen_ty_12,
|
||||||
|
pub task_fd_query: bpf_attr__bindgen_ty_13,
|
||||||
|
pub link_create: bpf_attr__bindgen_ty_14,
|
||||||
|
pub link_update: bpf_attr__bindgen_ty_15,
|
||||||
|
pub link_detach: bpf_attr__bindgen_ty_16,
|
||||||
|
pub enable_stats: bpf_attr__bindgen_ty_17,
|
||||||
|
pub iter_create: bpf_attr__bindgen_ty_18,
|
||||||
|
pub prog_bind_map: bpf_attr__bindgen_ty_19,
|
||||||
|
_bindgen_union_align: [u64; 15usize],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_1 {
|
||||||
|
pub map_type: __u32,
|
||||||
|
pub key_size: __u32,
|
||||||
|
pub value_size: __u32,
|
||||||
|
pub max_entries: __u32,
|
||||||
|
pub map_flags: __u32,
|
||||||
|
pub inner_map_fd: __u32,
|
||||||
|
pub numa_node: __u32,
|
||||||
|
pub map_name: [::std::os::raw::c_char; 16usize],
|
||||||
|
pub map_ifindex: __u32,
|
||||||
|
pub btf_fd: __u32,
|
||||||
|
pub btf_key_type_id: __u32,
|
||||||
|
pub btf_value_type_id: __u32,
|
||||||
|
pub btf_vmlinux_value_type_id: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_2 {
|
||||||
|
pub map_fd: __u32,
|
||||||
|
pub key: __u64,
|
||||||
|
pub __bindgen_anon_1: bpf_attr__bindgen_ty_2__bindgen_ty_1,
|
||||||
|
pub flags: __u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union bpf_attr__bindgen_ty_2__bindgen_ty_1 {
|
||||||
|
pub value: __u64,
|
||||||
|
pub next_key: __u64,
|
||||||
|
_bindgen_union_align: u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_3 {
|
||||||
|
pub in_batch: __u64,
|
||||||
|
pub out_batch: __u64,
|
||||||
|
pub keys: __u64,
|
||||||
|
pub values: __u64,
|
||||||
|
pub count: __u32,
|
||||||
|
pub map_fd: __u32,
|
||||||
|
pub elem_flags: __u64,
|
||||||
|
pub flags: __u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_4 {
|
||||||
|
pub prog_type: __u32,
|
||||||
|
pub insn_cnt: __u32,
|
||||||
|
pub insns: __u64,
|
||||||
|
pub license: __u64,
|
||||||
|
pub log_level: __u32,
|
||||||
|
pub log_size: __u32,
|
||||||
|
pub log_buf: __u64,
|
||||||
|
pub kern_version: __u32,
|
||||||
|
pub prog_flags: __u32,
|
||||||
|
pub prog_name: [::std::os::raw::c_char; 16usize],
|
||||||
|
pub prog_ifindex: __u32,
|
||||||
|
pub expected_attach_type: __u32,
|
||||||
|
pub prog_btf_fd: __u32,
|
||||||
|
pub func_info_rec_size: __u32,
|
||||||
|
pub func_info: __u64,
|
||||||
|
pub func_info_cnt: __u32,
|
||||||
|
pub line_info_rec_size: __u32,
|
||||||
|
pub line_info: __u64,
|
||||||
|
pub line_info_cnt: __u32,
|
||||||
|
pub attach_btf_id: __u32,
|
||||||
|
pub __bindgen_anon_1: bpf_attr__bindgen_ty_4__bindgen_ty_1,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union bpf_attr__bindgen_ty_4__bindgen_ty_1 {
|
||||||
|
pub attach_prog_fd: __u32,
|
||||||
|
pub attach_btf_obj_fd: __u32,
|
||||||
|
_bindgen_union_align: u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_5 {
|
||||||
|
pub pathname: __u64,
|
||||||
|
pub bpf_fd: __u32,
|
||||||
|
pub file_flags: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_6 {
|
||||||
|
pub target_fd: __u32,
|
||||||
|
pub attach_bpf_fd: __u32,
|
||||||
|
pub attach_type: __u32,
|
||||||
|
pub attach_flags: __u32,
|
||||||
|
pub replace_bpf_fd: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_7 {
|
||||||
|
pub prog_fd: __u32,
|
||||||
|
pub retval: __u32,
|
||||||
|
pub data_size_in: __u32,
|
||||||
|
pub data_size_out: __u32,
|
||||||
|
pub data_in: __u64,
|
||||||
|
pub data_out: __u64,
|
||||||
|
pub repeat: __u32,
|
||||||
|
pub duration: __u32,
|
||||||
|
pub ctx_size_in: __u32,
|
||||||
|
pub ctx_size_out: __u32,
|
||||||
|
pub ctx_in: __u64,
|
||||||
|
pub ctx_out: __u64,
|
||||||
|
pub flags: __u32,
|
||||||
|
pub cpu: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_8 {
|
||||||
|
pub __bindgen_anon_1: bpf_attr__bindgen_ty_8__bindgen_ty_1,
|
||||||
|
pub next_id: __u32,
|
||||||
|
pub open_flags: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union bpf_attr__bindgen_ty_8__bindgen_ty_1 {
|
||||||
|
pub start_id: __u32,
|
||||||
|
pub prog_id: __u32,
|
||||||
|
pub map_id: __u32,
|
||||||
|
pub btf_id: __u32,
|
||||||
|
pub link_id: __u32,
|
||||||
|
_bindgen_union_align: u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_9 {
|
||||||
|
pub bpf_fd: __u32,
|
||||||
|
pub info_len: __u32,
|
||||||
|
pub info: __u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_10 {
|
||||||
|
pub target_fd: __u32,
|
||||||
|
pub attach_type: __u32,
|
||||||
|
pub query_flags: __u32,
|
||||||
|
pub attach_flags: __u32,
|
||||||
|
pub prog_ids: __u64,
|
||||||
|
pub prog_cnt: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_11 {
|
||||||
|
pub name: __u64,
|
||||||
|
pub prog_fd: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_12 {
|
||||||
|
pub btf: __u64,
|
||||||
|
pub btf_log_buf: __u64,
|
||||||
|
pub btf_size: __u32,
|
||||||
|
pub btf_log_size: __u32,
|
||||||
|
pub btf_log_level: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_13 {
|
||||||
|
pub pid: __u32,
|
||||||
|
pub fd: __u32,
|
||||||
|
pub flags: __u32,
|
||||||
|
pub buf_len: __u32,
|
||||||
|
pub buf: __u64,
|
||||||
|
pub prog_id: __u32,
|
||||||
|
pub fd_type: __u32,
|
||||||
|
pub probe_offset: __u64,
|
||||||
|
pub probe_addr: __u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_14 {
|
||||||
|
pub prog_fd: __u32,
|
||||||
|
pub __bindgen_anon_1: bpf_attr__bindgen_ty_14__bindgen_ty_1,
|
||||||
|
pub attach_type: __u32,
|
||||||
|
pub flags: __u32,
|
||||||
|
pub __bindgen_anon_2: bpf_attr__bindgen_ty_14__bindgen_ty_2,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union bpf_attr__bindgen_ty_14__bindgen_ty_1 {
|
||||||
|
pub target_fd: __u32,
|
||||||
|
pub target_ifindex: __u32,
|
||||||
|
_bindgen_union_align: u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union bpf_attr__bindgen_ty_14__bindgen_ty_2 {
|
||||||
|
pub target_btf_id: __u32,
|
||||||
|
pub __bindgen_anon_1: bpf_attr__bindgen_ty_14__bindgen_ty_2__bindgen_ty_1,
|
||||||
|
_bindgen_union_align: [u64; 2usize],
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_14__bindgen_ty_2__bindgen_ty_1 {
|
||||||
|
pub iter_info: __u64,
|
||||||
|
pub iter_info_len: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_15 {
|
||||||
|
pub link_fd: __u32,
|
||||||
|
pub new_prog_fd: __u32,
|
||||||
|
pub flags: __u32,
|
||||||
|
pub old_prog_fd: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_16 {
|
||||||
|
pub link_fd: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_17 {
|
||||||
|
pub type_: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_18 {
|
||||||
|
pub link_fd: __u32,
|
||||||
|
pub flags: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct bpf_attr__bindgen_ty_19 {
|
||||||
|
pub prog_fd: __u32,
|
||||||
|
pub map_fd: __u32,
|
||||||
|
pub flags: __u32,
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
#![allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||||
|
|
||||||
|
// FIXME: generate for x86_64 and aarch64
|
||||||
|
|
||||||
|
mod bpf_bindings;
|
||||||
|
mod perf_bindings;
|
||||||
|
|
||||||
|
pub use bpf_bindings::*;
|
||||||
|
pub use perf_bindings::*;
|
@ -0,0 +1,917 @@
|
|||||||
|
/* automatically generated by rust-bindgen 0.55.1 */
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct __BindgenBitfieldUnit<Storage, Align> {
|
||||||
|
storage: Storage,
|
||||||
|
align: [Align; 0],
|
||||||
|
}
|
||||||
|
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align> {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(storage: Storage) -> Self {
|
||||||
|
Self { storage, align: [] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
|
||||||
|
where
|
||||||
|
Storage: AsRef<[u8]> + AsMut<[u8]>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
pub fn get_bit(&self, index: usize) -> bool {
|
||||||
|
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||||
|
let byte_index = index / 8;
|
||||||
|
let byte = self.storage.as_ref()[byte_index];
|
||||||
|
let bit_index = if cfg!(target_endian = "big") {
|
||||||
|
7 - (index % 8)
|
||||||
|
} else {
|
||||||
|
index % 8
|
||||||
|
};
|
||||||
|
let mask = 1 << bit_index;
|
||||||
|
byte & mask == mask
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_bit(&mut self, index: usize, val: bool) {
|
||||||
|
debug_assert!(index / 8 < self.storage.as_ref().len());
|
||||||
|
let byte_index = index / 8;
|
||||||
|
let byte = &mut self.storage.as_mut()[byte_index];
|
||||||
|
let bit_index = if cfg!(target_endian = "big") {
|
||||||
|
7 - (index % 8)
|
||||||
|
} else {
|
||||||
|
index % 8
|
||||||
|
};
|
||||||
|
let mask = 1 << bit_index;
|
||||||
|
if val {
|
||||||
|
*byte |= mask;
|
||||||
|
} else {
|
||||||
|
*byte &= !mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
|
||||||
|
debug_assert!(bit_width <= 64);
|
||||||
|
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||||
|
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||||
|
let mut val = 0;
|
||||||
|
for i in 0..(bit_width as usize) {
|
||||||
|
if self.get_bit(i + bit_offset) {
|
||||||
|
let index = if cfg!(target_endian = "big") {
|
||||||
|
bit_width as usize - 1 - i
|
||||||
|
} else {
|
||||||
|
i
|
||||||
|
};
|
||||||
|
val |= 1 << index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
|
||||||
|
debug_assert!(bit_width <= 64);
|
||||||
|
debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
|
||||||
|
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
|
||||||
|
for i in 0..(bit_width as usize) {
|
||||||
|
let mask = 1 << i;
|
||||||
|
let val_bit_is_set = val & mask == mask;
|
||||||
|
let index = if cfg!(target_endian = "big") {
|
||||||
|
bit_width as usize - 1 - i
|
||||||
|
} else {
|
||||||
|
i
|
||||||
|
};
|
||||||
|
self.set_bit(index + bit_offset, val_bit_is_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const PERF_FLAG_FD_NO_GROUP: u32 = 1;
|
||||||
|
pub const PERF_FLAG_FD_OUTPUT: u32 = 2;
|
||||||
|
pub const PERF_FLAG_PID_CGROUP: u32 = 4;
|
||||||
|
pub const PERF_FLAG_FD_CLOEXEC: u32 = 8;
|
||||||
|
pub type __u8 = ::std::os::raw::c_uchar;
|
||||||
|
pub type __u16 = ::std::os::raw::c_ushort;
|
||||||
|
pub type __s32 = ::std::os::raw::c_int;
|
||||||
|
pub type __u32 = ::std::os::raw::c_uint;
|
||||||
|
pub type __s64 = ::std::os::raw::c_longlong;
|
||||||
|
pub type __u64 = ::std::os::raw::c_ulonglong;
|
||||||
|
pub mod perf_type_id {
|
||||||
|
pub type Type = ::std::os::raw::c_uint;
|
||||||
|
pub const PERF_TYPE_HARDWARE: Type = 0;
|
||||||
|
pub const PERF_TYPE_SOFTWARE: Type = 1;
|
||||||
|
pub const PERF_TYPE_TRACEPOINT: Type = 2;
|
||||||
|
pub const PERF_TYPE_HW_CACHE: Type = 3;
|
||||||
|
pub const PERF_TYPE_RAW: Type = 4;
|
||||||
|
pub const PERF_TYPE_BREAKPOINT: Type = 5;
|
||||||
|
pub const PERF_TYPE_MAX: Type = 6;
|
||||||
|
}
|
||||||
|
pub mod perf_sw_ids {
|
||||||
|
pub type Type = ::std::os::raw::c_uint;
|
||||||
|
pub const PERF_COUNT_SW_CPU_CLOCK: Type = 0;
|
||||||
|
pub const PERF_COUNT_SW_TASK_CLOCK: Type = 1;
|
||||||
|
pub const PERF_COUNT_SW_PAGE_FAULTS: Type = 2;
|
||||||
|
pub const PERF_COUNT_SW_CONTEXT_SWITCHES: Type = 3;
|
||||||
|
pub const PERF_COUNT_SW_CPU_MIGRATIONS: Type = 4;
|
||||||
|
pub const PERF_COUNT_SW_PAGE_FAULTS_MIN: Type = 5;
|
||||||
|
pub const PERF_COUNT_SW_PAGE_FAULTS_MAJ: Type = 6;
|
||||||
|
pub const PERF_COUNT_SW_ALIGNMENT_FAULTS: Type = 7;
|
||||||
|
pub const PERF_COUNT_SW_EMULATION_FAULTS: Type = 8;
|
||||||
|
pub const PERF_COUNT_SW_DUMMY: Type = 9;
|
||||||
|
pub const PERF_COUNT_SW_BPF_OUTPUT: Type = 10;
|
||||||
|
pub const PERF_COUNT_SW_MAX: Type = 11;
|
||||||
|
}
|
||||||
|
pub mod perf_event_sample_format {
|
||||||
|
pub type Type = ::std::os::raw::c_ulong;
|
||||||
|
pub const PERF_SAMPLE_IP: Type = 1;
|
||||||
|
pub const PERF_SAMPLE_TID: Type = 2;
|
||||||
|
pub const PERF_SAMPLE_TIME: Type = 4;
|
||||||
|
pub const PERF_SAMPLE_ADDR: Type = 8;
|
||||||
|
pub const PERF_SAMPLE_READ: Type = 16;
|
||||||
|
pub const PERF_SAMPLE_CALLCHAIN: Type = 32;
|
||||||
|
pub const PERF_SAMPLE_ID: Type = 64;
|
||||||
|
pub const PERF_SAMPLE_CPU: Type = 128;
|
||||||
|
pub const PERF_SAMPLE_PERIOD: Type = 256;
|
||||||
|
pub const PERF_SAMPLE_STREAM_ID: Type = 512;
|
||||||
|
pub const PERF_SAMPLE_RAW: Type = 1024;
|
||||||
|
pub const PERF_SAMPLE_BRANCH_STACK: Type = 2048;
|
||||||
|
pub const PERF_SAMPLE_REGS_USER: Type = 4096;
|
||||||
|
pub const PERF_SAMPLE_STACK_USER: Type = 8192;
|
||||||
|
pub const PERF_SAMPLE_WEIGHT: Type = 16384;
|
||||||
|
pub const PERF_SAMPLE_DATA_SRC: Type = 32768;
|
||||||
|
pub const PERF_SAMPLE_IDENTIFIER: Type = 65536;
|
||||||
|
pub const PERF_SAMPLE_TRANSACTION: Type = 131072;
|
||||||
|
pub const PERF_SAMPLE_REGS_INTR: Type = 262144;
|
||||||
|
pub const PERF_SAMPLE_PHYS_ADDR: Type = 524288;
|
||||||
|
pub const PERF_SAMPLE_AUX: Type = 1048576;
|
||||||
|
pub const PERF_SAMPLE_CGROUP: Type = 2097152;
|
||||||
|
pub const PERF_SAMPLE_MAX: Type = 4194304;
|
||||||
|
pub const __PERF_SAMPLE_CALLCHAIN_EARLY: Type = 9223372036854775808;
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct perf_event_attr {
|
||||||
|
pub type_: __u32,
|
||||||
|
pub size: __u32,
|
||||||
|
pub config: __u64,
|
||||||
|
pub __bindgen_anon_1: perf_event_attr__bindgen_ty_1,
|
||||||
|
pub sample_type: __u64,
|
||||||
|
pub read_format: __u64,
|
||||||
|
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize], u32>,
|
||||||
|
pub __bindgen_anon_2: perf_event_attr__bindgen_ty_2,
|
||||||
|
pub bp_type: __u32,
|
||||||
|
pub __bindgen_anon_3: perf_event_attr__bindgen_ty_3,
|
||||||
|
pub __bindgen_anon_4: perf_event_attr__bindgen_ty_4,
|
||||||
|
pub branch_sample_type: __u64,
|
||||||
|
pub sample_regs_user: __u64,
|
||||||
|
pub sample_stack_user: __u32,
|
||||||
|
pub clockid: __s32,
|
||||||
|
pub sample_regs_intr: __u64,
|
||||||
|
pub aux_watermark: __u32,
|
||||||
|
pub sample_max_stack: __u16,
|
||||||
|
pub __reserved_2: __u16,
|
||||||
|
pub aux_sample_size: __u32,
|
||||||
|
pub __reserved_3: __u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union perf_event_attr__bindgen_ty_1 {
|
||||||
|
pub sample_period: __u64,
|
||||||
|
pub sample_freq: __u64,
|
||||||
|
_bindgen_union_align: u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union perf_event_attr__bindgen_ty_2 {
|
||||||
|
pub wakeup_events: __u32,
|
||||||
|
pub wakeup_watermark: __u32,
|
||||||
|
_bindgen_union_align: u32,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union perf_event_attr__bindgen_ty_3 {
|
||||||
|
pub bp_addr: __u64,
|
||||||
|
pub kprobe_func: __u64,
|
||||||
|
pub uprobe_path: __u64,
|
||||||
|
pub config1: __u64,
|
||||||
|
_bindgen_union_align: u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union perf_event_attr__bindgen_ty_4 {
|
||||||
|
pub bp_len: __u64,
|
||||||
|
pub kprobe_addr: __u64,
|
||||||
|
pub probe_offset: __u64,
|
||||||
|
pub config2: __u64,
|
||||||
|
_bindgen_union_align: u64,
|
||||||
|
}
|
||||||
|
impl perf_event_attr {
|
||||||
|
#[inline]
|
||||||
|
pub fn disabled(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_disabled(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(0usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn inherit(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_inherit(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(1usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn pinned(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_pinned(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(2usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclusive(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclusive(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(3usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclude_user(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclude_user(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(4usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclude_kernel(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclude_kernel(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(5usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclude_hv(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclude_hv(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(6usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclude_idle(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclude_idle(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(7usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn mmap(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_mmap(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(8usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn comm(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(9usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_comm(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(9usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn freq(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(10usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_freq(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(10usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn inherit_stat(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(11usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_inherit_stat(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(11usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn enable_on_exec(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_enable_on_exec(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(12usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn task(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(13usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_task(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(13usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn watermark(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(14usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_watermark(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(14usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn precise_ip(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(15usize, 2u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_precise_ip(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(15usize, 2u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn mmap_data(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(17usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_mmap_data(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(17usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn sample_id_all(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(18usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_sample_id_all(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(18usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclude_host(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(19usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclude_host(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(19usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclude_guest(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(20usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclude_guest(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(20usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclude_callchain_kernel(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(21usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclude_callchain_kernel(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(21usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn exclude_callchain_user(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(22usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_exclude_callchain_user(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(22usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn mmap2(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(23usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_mmap2(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(23usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn comm_exec(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_comm_exec(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(24usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn use_clockid(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(25usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_use_clockid(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(25usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn context_switch(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(26usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_context_switch(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(26usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn write_backward(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(27usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_write_backward(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(27usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn namespaces(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(28usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_namespaces(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(28usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn ksymbol(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(29usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_ksymbol(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(29usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn bpf_event(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(30usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_bpf_event(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(30usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn aux_output(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(31usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_aux_output(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(31usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn cgroup(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(32usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cgroup(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(32usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn __reserved_1(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(33usize, 31u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set___reserved_1(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(33usize, 31u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn new_bitfield_1(
|
||||||
|
disabled: __u64,
|
||||||
|
inherit: __u64,
|
||||||
|
pinned: __u64,
|
||||||
|
exclusive: __u64,
|
||||||
|
exclude_user: __u64,
|
||||||
|
exclude_kernel: __u64,
|
||||||
|
exclude_hv: __u64,
|
||||||
|
exclude_idle: __u64,
|
||||||
|
mmap: __u64,
|
||||||
|
comm: __u64,
|
||||||
|
freq: __u64,
|
||||||
|
inherit_stat: __u64,
|
||||||
|
enable_on_exec: __u64,
|
||||||
|
task: __u64,
|
||||||
|
watermark: __u64,
|
||||||
|
precise_ip: __u64,
|
||||||
|
mmap_data: __u64,
|
||||||
|
sample_id_all: __u64,
|
||||||
|
exclude_host: __u64,
|
||||||
|
exclude_guest: __u64,
|
||||||
|
exclude_callchain_kernel: __u64,
|
||||||
|
exclude_callchain_user: __u64,
|
||||||
|
mmap2: __u64,
|
||||||
|
comm_exec: __u64,
|
||||||
|
use_clockid: __u64,
|
||||||
|
context_switch: __u64,
|
||||||
|
write_backward: __u64,
|
||||||
|
namespaces: __u64,
|
||||||
|
ksymbol: __u64,
|
||||||
|
bpf_event: __u64,
|
||||||
|
aux_output: __u64,
|
||||||
|
cgroup: __u64,
|
||||||
|
__reserved_1: __u64,
|
||||||
|
) -> __BindgenBitfieldUnit<[u8; 8usize], u32> {
|
||||||
|
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize], u32> =
|
||||||
|
Default::default();
|
||||||
|
__bindgen_bitfield_unit.set(0usize, 1u8, {
|
||||||
|
let disabled: u64 = unsafe { ::std::mem::transmute(disabled) };
|
||||||
|
disabled as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(1usize, 1u8, {
|
||||||
|
let inherit: u64 = unsafe { ::std::mem::transmute(inherit) };
|
||||||
|
inherit as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(2usize, 1u8, {
|
||||||
|
let pinned: u64 = unsafe { ::std::mem::transmute(pinned) };
|
||||||
|
pinned as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(3usize, 1u8, {
|
||||||
|
let exclusive: u64 = unsafe { ::std::mem::transmute(exclusive) };
|
||||||
|
exclusive as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(4usize, 1u8, {
|
||||||
|
let exclude_user: u64 = unsafe { ::std::mem::transmute(exclude_user) };
|
||||||
|
exclude_user as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(5usize, 1u8, {
|
||||||
|
let exclude_kernel: u64 = unsafe { ::std::mem::transmute(exclude_kernel) };
|
||||||
|
exclude_kernel as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(6usize, 1u8, {
|
||||||
|
let exclude_hv: u64 = unsafe { ::std::mem::transmute(exclude_hv) };
|
||||||
|
exclude_hv as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(7usize, 1u8, {
|
||||||
|
let exclude_idle: u64 = unsafe { ::std::mem::transmute(exclude_idle) };
|
||||||
|
exclude_idle as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(8usize, 1u8, {
|
||||||
|
let mmap: u64 = unsafe { ::std::mem::transmute(mmap) };
|
||||||
|
mmap as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(9usize, 1u8, {
|
||||||
|
let comm: u64 = unsafe { ::std::mem::transmute(comm) };
|
||||||
|
comm as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(10usize, 1u8, {
|
||||||
|
let freq: u64 = unsafe { ::std::mem::transmute(freq) };
|
||||||
|
freq as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(11usize, 1u8, {
|
||||||
|
let inherit_stat: u64 = unsafe { ::std::mem::transmute(inherit_stat) };
|
||||||
|
inherit_stat as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(12usize, 1u8, {
|
||||||
|
let enable_on_exec: u64 = unsafe { ::std::mem::transmute(enable_on_exec) };
|
||||||
|
enable_on_exec as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(13usize, 1u8, {
|
||||||
|
let task: u64 = unsafe { ::std::mem::transmute(task) };
|
||||||
|
task as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(14usize, 1u8, {
|
||||||
|
let watermark: u64 = unsafe { ::std::mem::transmute(watermark) };
|
||||||
|
watermark as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(15usize, 2u8, {
|
||||||
|
let precise_ip: u64 = unsafe { ::std::mem::transmute(precise_ip) };
|
||||||
|
precise_ip as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(17usize, 1u8, {
|
||||||
|
let mmap_data: u64 = unsafe { ::std::mem::transmute(mmap_data) };
|
||||||
|
mmap_data as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(18usize, 1u8, {
|
||||||
|
let sample_id_all: u64 = unsafe { ::std::mem::transmute(sample_id_all) };
|
||||||
|
sample_id_all as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(19usize, 1u8, {
|
||||||
|
let exclude_host: u64 = unsafe { ::std::mem::transmute(exclude_host) };
|
||||||
|
exclude_host as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(20usize, 1u8, {
|
||||||
|
let exclude_guest: u64 = unsafe { ::std::mem::transmute(exclude_guest) };
|
||||||
|
exclude_guest as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(21usize, 1u8, {
|
||||||
|
let exclude_callchain_kernel: u64 =
|
||||||
|
unsafe { ::std::mem::transmute(exclude_callchain_kernel) };
|
||||||
|
exclude_callchain_kernel as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(22usize, 1u8, {
|
||||||
|
let exclude_callchain_user: u64 =
|
||||||
|
unsafe { ::std::mem::transmute(exclude_callchain_user) };
|
||||||
|
exclude_callchain_user as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(23usize, 1u8, {
|
||||||
|
let mmap2: u64 = unsafe { ::std::mem::transmute(mmap2) };
|
||||||
|
mmap2 as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(24usize, 1u8, {
|
||||||
|
let comm_exec: u64 = unsafe { ::std::mem::transmute(comm_exec) };
|
||||||
|
comm_exec as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(25usize, 1u8, {
|
||||||
|
let use_clockid: u64 = unsafe { ::std::mem::transmute(use_clockid) };
|
||||||
|
use_clockid as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(26usize, 1u8, {
|
||||||
|
let context_switch: u64 = unsafe { ::std::mem::transmute(context_switch) };
|
||||||
|
context_switch as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(27usize, 1u8, {
|
||||||
|
let write_backward: u64 = unsafe { ::std::mem::transmute(write_backward) };
|
||||||
|
write_backward as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(28usize, 1u8, {
|
||||||
|
let namespaces: u64 = unsafe { ::std::mem::transmute(namespaces) };
|
||||||
|
namespaces as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(29usize, 1u8, {
|
||||||
|
let ksymbol: u64 = unsafe { ::std::mem::transmute(ksymbol) };
|
||||||
|
ksymbol as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(30usize, 1u8, {
|
||||||
|
let bpf_event: u64 = unsafe { ::std::mem::transmute(bpf_event) };
|
||||||
|
bpf_event as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(31usize, 1u8, {
|
||||||
|
let aux_output: u64 = unsafe { ::std::mem::transmute(aux_output) };
|
||||||
|
aux_output as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(32usize, 1u8, {
|
||||||
|
let cgroup: u64 = unsafe { ::std::mem::transmute(cgroup) };
|
||||||
|
cgroup as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(33usize, 31u8, {
|
||||||
|
let __reserved_1: u64 = unsafe { ::std::mem::transmute(__reserved_1) };
|
||||||
|
__reserved_1 as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct perf_event_mmap_page {
|
||||||
|
pub version: __u32,
|
||||||
|
pub compat_version: __u32,
|
||||||
|
pub lock: __u32,
|
||||||
|
pub index: __u32,
|
||||||
|
pub offset: __s64,
|
||||||
|
pub time_enabled: __u64,
|
||||||
|
pub time_running: __u64,
|
||||||
|
pub __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1,
|
||||||
|
pub pmc_width: __u16,
|
||||||
|
pub time_shift: __u16,
|
||||||
|
pub time_mult: __u32,
|
||||||
|
pub time_offset: __u64,
|
||||||
|
pub time_zero: __u64,
|
||||||
|
pub size: __u32,
|
||||||
|
pub __reserved: [__u8; 948usize],
|
||||||
|
pub data_head: __u64,
|
||||||
|
pub data_tail: __u64,
|
||||||
|
pub data_offset: __u64,
|
||||||
|
pub data_size: __u64,
|
||||||
|
pub aux_head: __u64,
|
||||||
|
pub aux_tail: __u64,
|
||||||
|
pub aux_offset: __u64,
|
||||||
|
pub aux_size: __u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub union perf_event_mmap_page__bindgen_ty_1 {
|
||||||
|
pub capabilities: __u64,
|
||||||
|
pub __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1,
|
||||||
|
_bindgen_union_align: u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[repr(align(8))]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1 {
|
||||||
|
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize], u64>,
|
||||||
|
}
|
||||||
|
impl perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1 {
|
||||||
|
#[inline]
|
||||||
|
pub fn cap_bit0(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cap_bit0(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(0usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn cap_bit0_is_deprecated(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cap_bit0_is_deprecated(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(1usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn cap_user_rdpmc(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cap_user_rdpmc(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(2usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn cap_user_time(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cap_user_time(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(3usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn cap_user_time_zero(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cap_user_time_zero(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(4usize, 1u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn cap_____res(&self) -> __u64 {
|
||||||
|
unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 59u8) as u64) }
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cap_____res(&mut self, val: __u64) {
|
||||||
|
unsafe {
|
||||||
|
let val: u64 = ::std::mem::transmute(val);
|
||||||
|
self._bitfield_1.set(5usize, 59u8, val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn new_bitfield_1(
|
||||||
|
cap_bit0: __u64,
|
||||||
|
cap_bit0_is_deprecated: __u64,
|
||||||
|
cap_user_rdpmc: __u64,
|
||||||
|
cap_user_time: __u64,
|
||||||
|
cap_user_time_zero: __u64,
|
||||||
|
cap_____res: __u64,
|
||||||
|
) -> __BindgenBitfieldUnit<[u8; 8usize], u64> {
|
||||||
|
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize], u64> =
|
||||||
|
Default::default();
|
||||||
|
__bindgen_bitfield_unit.set(0usize, 1u8, {
|
||||||
|
let cap_bit0: u64 = unsafe { ::std::mem::transmute(cap_bit0) };
|
||||||
|
cap_bit0 as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(1usize, 1u8, {
|
||||||
|
let cap_bit0_is_deprecated: u64 =
|
||||||
|
unsafe { ::std::mem::transmute(cap_bit0_is_deprecated) };
|
||||||
|
cap_bit0_is_deprecated as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(2usize, 1u8, {
|
||||||
|
let cap_user_rdpmc: u64 = unsafe { ::std::mem::transmute(cap_user_rdpmc) };
|
||||||
|
cap_user_rdpmc as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(3usize, 1u8, {
|
||||||
|
let cap_user_time: u64 = unsafe { ::std::mem::transmute(cap_user_time) };
|
||||||
|
cap_user_time as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(4usize, 1u8, {
|
||||||
|
let cap_user_time_zero: u64 = unsafe { ::std::mem::transmute(cap_user_time_zero) };
|
||||||
|
cap_user_time_zero as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit.set(5usize, 59u8, {
|
||||||
|
let cap_____res: u64 = unsafe { ::std::mem::transmute(cap_____res) };
|
||||||
|
cap_____res as u64
|
||||||
|
});
|
||||||
|
__bindgen_bitfield_unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct perf_event_header {
|
||||||
|
pub type_: __u32,
|
||||||
|
pub misc: __u16,
|
||||||
|
pub size: __u16,
|
||||||
|
}
|
||||||
|
pub mod perf_event_type {
|
||||||
|
pub type Type = ::std::os::raw::c_uint;
|
||||||
|
pub const PERF_RECORD_MMAP: Type = 1;
|
||||||
|
pub const PERF_RECORD_LOST: Type = 2;
|
||||||
|
pub const PERF_RECORD_COMM: Type = 3;
|
||||||
|
pub const PERF_RECORD_EXIT: Type = 4;
|
||||||
|
pub const PERF_RECORD_THROTTLE: Type = 5;
|
||||||
|
pub const PERF_RECORD_UNTHROTTLE: Type = 6;
|
||||||
|
pub const PERF_RECORD_FORK: Type = 7;
|
||||||
|
pub const PERF_RECORD_READ: Type = 8;
|
||||||
|
pub const PERF_RECORD_SAMPLE: Type = 9;
|
||||||
|
pub const PERF_RECORD_MMAP2: Type = 10;
|
||||||
|
pub const PERF_RECORD_AUX: Type = 11;
|
||||||
|
pub const PERF_RECORD_ITRACE_START: Type = 12;
|
||||||
|
pub const PERF_RECORD_LOST_SAMPLES: Type = 13;
|
||||||
|
pub const PERF_RECORD_SWITCH: Type = 14;
|
||||||
|
pub const PERF_RECORD_SWITCH_CPU_WIDE: Type = 15;
|
||||||
|
pub const PERF_RECORD_NAMESPACES: Type = 16;
|
||||||
|
pub const PERF_RECORD_KSYMBOL: Type = 17;
|
||||||
|
pub const PERF_RECORD_BPF_EVENT: Type = 18;
|
||||||
|
pub const PERF_RECORD_CGROUP: Type = 19;
|
||||||
|
pub const PERF_RECORD_MAX: Type = 20;
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#![deny(clippy::all)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
mod bpf;
|
||||||
|
mod generated;
|
||||||
|
pub mod maps;
|
||||||
|
mod obj;
|
||||||
|
pub mod programs;
|
||||||
|
mod syscalls;
|
||||||
|
|
||||||
|
pub use bpf::*;
|
@ -0,0 +1,725 @@
|
|||||||
|
use std::{convert::TryFrom, marker::PhantomData, mem};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
generated::bpf_map_type::BPF_MAP_TYPE_HASH,
|
||||||
|
syscalls::{
|
||||||
|
bpf_map_delete_elem, bpf_map_get_next_key, bpf_map_lookup_and_delete_elem,
|
||||||
|
bpf_map_lookup_elem, bpf_map_update_elem,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Map, MapError};
|
||||||
|
use crate::Pod;
|
||||||
|
|
||||||
|
pub struct HashMap<T: AsRef<Map>, K, V> {
|
||||||
|
inner: T,
|
||||||
|
_k: PhantomData<K>,
|
||||||
|
_v: PhantomData<V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<Map>, K: Pod, V: Pod> HashMap<T, K, V> {
|
||||||
|
pub fn new(map: T) -> Result<HashMap<T, K, V>, MapError> {
|
||||||
|
let inner = map.as_ref();
|
||||||
|
let map_type = inner.obj.def.map_type;
|
||||||
|
if map_type != BPF_MAP_TYPE_HASH {
|
||||||
|
return Err(MapError::InvalidMapType {
|
||||||
|
map_type: map_type as u32,
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
let size = mem::size_of::<K>();
|
||||||
|
let expected = inner.obj.def.key_size as usize;
|
||||||
|
if size != expected {
|
||||||
|
return Err(MapError::InvalidKeySize { size, expected });
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = mem::size_of::<V>();
|
||||||
|
let expected = inner.obj.def.value_size as usize;
|
||||||
|
if size != expected {
|
||||||
|
return Err(MapError::InvalidValueSize { size, expected });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HashMap {
|
||||||
|
inner: map,
|
||||||
|
_k: PhantomData,
|
||||||
|
_v: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn get(&self, key: &K, flags: u64) -> Result<Option<V>, MapError> {
|
||||||
|
let fd = self.inner.as_ref().fd_or_err()?;
|
||||||
|
bpf_map_lookup_elem(fd, key, flags)
|
||||||
|
.map_err(|(code, io_error)| MapError::LookupElementFailed { code, io_error })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn iter<'coll>(&'coll self) -> MapIter<'coll, T, K, V> {
|
||||||
|
MapIter::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn keys<'coll>(&'coll self) -> MapKeys<'coll, T, K, V> {
|
||||||
|
MapKeys::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<Map> + AsMut<Map>, K: Pod, V: Pod> HashMap<T, K, V> {
|
||||||
|
pub fn insert(&mut self, key: K, value: V, flags: u64) -> Result<(), MapError> {
|
||||||
|
let fd = self.inner.as_ref().fd_or_err()?;
|
||||||
|
bpf_map_update_elem(fd, &key, &value, flags)
|
||||||
|
.map_err(|(code, io_error)| MapError::UpdateElementFailed { code, io_error })?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn pop(&mut self, key: &K) -> Result<Option<V>, MapError> {
|
||||||
|
let fd = self.inner.as_ref().fd_or_err()?;
|
||||||
|
bpf_map_lookup_and_delete_elem(fd, key)
|
||||||
|
.map_err(|(code, io_error)| MapError::LookupAndDeleteElementFailed { code, io_error })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, key: &K) -> Result<(), MapError> {
|
||||||
|
let fd = self.inner.as_ref().fd_or_err()?;
|
||||||
|
bpf_map_delete_elem(fd, key)
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|(code, io_error)| MapError::DeleteElementFailed { code, io_error })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K: Pod, V: Pod> TryFrom<&'a Map> for HashMap<&'a Map, K, V> {
|
||||||
|
type Error = MapError;
|
||||||
|
|
||||||
|
fn try_from(inner: &'a Map) -> Result<HashMap<&'a Map, K, V>, MapError> {
|
||||||
|
HashMap::new(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K: Pod, V: Pod> TryFrom<&'a mut Map> for HashMap<&'a mut Map, K, V> {
|
||||||
|
type Error = MapError;
|
||||||
|
|
||||||
|
fn try_from(inner: &'a mut Map) -> Result<HashMap<&'a mut Map, K, V>, MapError> {
|
||||||
|
HashMap::new(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MapKeys<'coll, T: AsRef<Map>, K: Clone + Pod, V: Clone + Pod> {
|
||||||
|
map: &'coll HashMap<T, K, V>,
|
||||||
|
err: bool,
|
||||||
|
key: Option<K>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'coll, T: AsRef<Map>, K: Clone + Pod, V: Clone + Pod> MapKeys<'coll, T, K, V> {
|
||||||
|
fn new(map: &'coll HashMap<T, K, V>) -> MapKeys<'coll, T, K, V> {
|
||||||
|
MapKeys {
|
||||||
|
map,
|
||||||
|
err: false,
|
||||||
|
key: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<Map>, K: Clone + Pod, V: Clone + Pod> Iterator for MapKeys<'_, T, K, V> {
|
||||||
|
type Item = Result<K, MapError>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Result<K, MapError>> {
|
||||||
|
if self.err {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fd = match self.map.inner.as_ref().fd_or_err() {
|
||||||
|
Ok(fd) => fd,
|
||||||
|
Err(e) => {
|
||||||
|
self.err = true;
|
||||||
|
return Some(Err(e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match bpf_map_get_next_key(fd, self.key.as_ref()) {
|
||||||
|
Ok(Some(key)) => {
|
||||||
|
self.key = Some(key);
|
||||||
|
return Some(Ok(key));
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
self.key = None;
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Err((code, io_error)) => {
|
||||||
|
self.err = true;
|
||||||
|
return Some(Err(MapError::GetNextKeyFailed { code, io_error }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MapIter<'coll, T: AsRef<Map>, K: Clone + Pod, V: Clone + Pod> {
|
||||||
|
inner: MapKeys<'coll, T, K, V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'coll, T: AsRef<Map>, K: Clone + Pod, V: Clone + Pod> MapIter<'coll, T, K, V> {
|
||||||
|
fn new(map: &'coll HashMap<T, K, V>) -> MapIter<'coll, T, K, V> {
|
||||||
|
MapIter {
|
||||||
|
inner: MapKeys::new(map),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<Map>, K: Clone + Pod, V: Clone + Pod> Iterator for MapIter<'_, T, K, V> {
|
||||||
|
type Item = Result<(K, V), MapError>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
match self.inner.next() {
|
||||||
|
Some(Ok(key)) => {
|
||||||
|
let value = unsafe { self.inner.map.get(&key, 0) };
|
||||||
|
match value {
|
||||||
|
Ok(None) => continue,
|
||||||
|
Ok(Some(value)) => return Some(Ok((key, value))),
|
||||||
|
Err(e) => return Some(Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Err(e)) => return Some(Err(e)),
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<Map> for &Map {
|
||||||
|
fn as_ref(&self) -> &Map {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<Map> for &mut Map {
|
||||||
|
fn as_ref(&self) -> &Map {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMut<Map> for &mut Map {
|
||||||
|
fn as_mut(&mut self) -> &mut Map {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use libc::{EFAULT, ENOENT};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bpf_map_def,
|
||||||
|
generated::{
|
||||||
|
bpf_attr, bpf_cmd,
|
||||||
|
bpf_map_type::{BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERF_EVENT_ARRAY},
|
||||||
|
},
|
||||||
|
obj,
|
||||||
|
syscalls::{override_syscall, SysResult, Syscall},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn new_obj_map(name: &str) -> obj::Map {
|
||||||
|
obj::Map {
|
||||||
|
name: name.to_string(),
|
||||||
|
def: bpf_map_def {
|
||||||
|
map_type: BPF_MAP_TYPE_HASH,
|
||||||
|
key_size: 4,
|
||||||
|
value_size: 4,
|
||||||
|
max_entries: 1024,
|
||||||
|
map_flags: 0,
|
||||||
|
},
|
||||||
|
section_index: 0,
|
||||||
|
data: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sys_error(value: i32) -> SysResult {
|
||||||
|
Err((-1, io::Error::from_raw_os_error(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wrong_key_size() {
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: None,
|
||||||
|
};
|
||||||
|
assert!(matches!(
|
||||||
|
HashMap::<_, u8, u32>::new(&map),
|
||||||
|
Err(MapError::InvalidKeySize {
|
||||||
|
size: 1,
|
||||||
|
expected: 4
|
||||||
|
})
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wrong_value_size() {
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: None,
|
||||||
|
};
|
||||||
|
assert!(matches!(
|
||||||
|
HashMap::<_, u32, u16>::new(&map),
|
||||||
|
Err(MapError::InvalidValueSize {
|
||||||
|
size: 2,
|
||||||
|
expected: 4
|
||||||
|
})
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_from_wrong_map() {
|
||||||
|
let map = Map {
|
||||||
|
obj: obj::Map {
|
||||||
|
name: "TEST".to_string(),
|
||||||
|
def: bpf_map_def {
|
||||||
|
map_type: BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
||||||
|
key_size: 4,
|
||||||
|
value_size: 4,
|
||||||
|
max_entries: 1024,
|
||||||
|
map_flags: 0,
|
||||||
|
},
|
||||||
|
section_index: 0,
|
||||||
|
data: Vec::new(),
|
||||||
|
},
|
||||||
|
fd: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
HashMap::<_, u32, u32>::try_from(&map),
|
||||||
|
Err(MapError::InvalidMapType { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_from_ok() {
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: None,
|
||||||
|
};
|
||||||
|
assert!(HashMap::<_, u32, u32>::try_from(&map).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert_not_created() {
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: None,
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
hm.insert(1, 42, 0),
|
||||||
|
Err(MapError::NotCreated { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert_syscall_error() {
|
||||||
|
override_syscall(|_| sys_error(EFAULT));
|
||||||
|
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
hm.insert(1, 42, 0),
|
||||||
|
Err(MapError::UpdateElementFailed { code: -1, io_error }) if io_error.raw_os_error() == Some(EFAULT)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert_ok() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_UPDATE_ELEM,
|
||||||
|
..
|
||||||
|
} => Ok(1),
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(hm.insert(1, 42, 0).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_remove_not_created() {
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: None,
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(hm.remove(&1), Err(MapError::NotCreated { .. })));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_remove_syscall_error() {
|
||||||
|
override_syscall(|_| sys_error(EFAULT));
|
||||||
|
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
hm.remove(&1),
|
||||||
|
Err(MapError::DeleteElementFailed { code: -1, io_error }) if io_error.raw_os_error() == Some(EFAULT)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_remove_ok() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_DELETE_ELEM,
|
||||||
|
..
|
||||||
|
} => Ok(1),
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(hm.remove(&1).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_not_created() {
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: None,
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
unsafe { hm.get(&1, 0) },
|
||||||
|
Err(MapError::NotCreated { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_syscall_error() {
|
||||||
|
override_syscall(|_| sys_error(EFAULT));
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
unsafe { hm.get(&1, 0) },
|
||||||
|
Err(MapError::LookupElementFailed { code: -1, io_error }) if io_error.raw_os_error() == Some(EFAULT)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_not_found() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
|
||||||
|
..
|
||||||
|
} => sys_error(ENOENT),
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(unsafe { hm.get(&1, 0) }, Ok(None)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pop_not_created() {
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: None,
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
unsafe { hm.pop(&1) },
|
||||||
|
Err(MapError::NotCreated { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pop_syscall_error() {
|
||||||
|
override_syscall(|_| sys_error(EFAULT));
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
unsafe { hm.pop(&1) },
|
||||||
|
Err(MapError::LookupAndDeleteElementFailed { code: -1, io_error }) if io_error.raw_os_error() == Some(EFAULT)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pop_not_found() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_LOOKUP_AND_DELETE_ELEM,
|
||||||
|
..
|
||||||
|
} => sys_error(ENOENT),
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
let mut map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let mut hm = HashMap::<_, u32, u32>::new(&mut map).unwrap();
|
||||||
|
|
||||||
|
assert!(matches!(unsafe { hm.pop(&1) }, Ok(None)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bpf_key<T: Copy>(attr: &bpf_attr) -> Option<T> {
|
||||||
|
match unsafe { attr.__bindgen_anon_2.key } as *const T {
|
||||||
|
p if p.is_null() => None,
|
||||||
|
p => Some(unsafe { *p }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_next_key<T: Copy>(attr: &bpf_attr, next: T) {
|
||||||
|
let key = unsafe { attr.__bindgen_anon_2.__bindgen_anon_1.next_key } as *const T as *mut T;
|
||||||
|
unsafe { *key = next };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_ret<T: Copy>(attr: &bpf_attr, ret: T) {
|
||||||
|
let value = unsafe { attr.__bindgen_anon_2.__bindgen_anon_1.value } as *const T as *mut T;
|
||||||
|
unsafe { *value = ret };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keys_empty() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
|
||||||
|
..
|
||||||
|
} => sys_error(ENOENT),
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
let keys = unsafe { hm.keys() }.collect::<Result<Vec<_>, _>>();
|
||||||
|
assert!(matches!(keys, Ok(ks) if ks.is_empty()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_next_key(attr: &bpf_attr) -> SysResult {
|
||||||
|
match bpf_key(&attr) {
|
||||||
|
None => set_next_key(&attr, 10),
|
||||||
|
Some(10) => set_next_key(&attr, 20),
|
||||||
|
Some(20) => set_next_key(&attr, 30),
|
||||||
|
Some(30) => return sys_error(ENOENT),
|
||||||
|
Some(_) => return sys_error(EFAULT),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_elem(attr: &bpf_attr) -> SysResult {
|
||||||
|
match bpf_key(&attr) {
|
||||||
|
Some(10) => set_ret(&attr, 100),
|
||||||
|
Some(20) => set_ret(&attr, 200),
|
||||||
|
Some(30) => set_ret(&attr, 300),
|
||||||
|
Some(_) => return sys_error(ENOENT),
|
||||||
|
None => return sys_error(EFAULT),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keys() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
|
||||||
|
attr,
|
||||||
|
} => get_next_key(&attr),
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
|
||||||
|
let keys = unsafe { hm.keys() }.collect::<Result<Vec<_>, _>>().unwrap();
|
||||||
|
assert_eq!(&keys, &[10, 20, 30])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keys_error() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
|
||||||
|
attr,
|
||||||
|
} => {
|
||||||
|
match bpf_key(&attr) {
|
||||||
|
None => set_next_key(&attr, 10),
|
||||||
|
Some(10) => set_next_key(&attr, 20),
|
||||||
|
Some(_) => return sys_error(EFAULT),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
|
||||||
|
let mut keys = unsafe { hm.keys() };
|
||||||
|
assert!(matches!(keys.next(), Some(Ok(10))));
|
||||||
|
assert!(matches!(keys.next(), Some(Ok(20))));
|
||||||
|
assert!(matches!(keys.next(), Some(Err(MapError::GetNextKeyFailed { .. }))));
|
||||||
|
assert!(matches!(keys.next(), None));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iter() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
|
||||||
|
attr,
|
||||||
|
} => get_next_key(&attr),
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
|
||||||
|
attr,
|
||||||
|
} => lookup_elem(&attr),
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
let items = unsafe { hm.iter() }.collect::<Result<Vec<_>, _>>().unwrap();
|
||||||
|
assert_eq!(&items, &[(10, 100), (20, 200), (30, 300)])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iter_key_deleted() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
|
||||||
|
attr,
|
||||||
|
} => get_next_key(&attr),
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
|
||||||
|
attr,
|
||||||
|
} => {
|
||||||
|
match bpf_key(&attr) {
|
||||||
|
Some(10) => set_ret(&attr, 100),
|
||||||
|
Some(20) => return sys_error(ENOENT),
|
||||||
|
Some(30) => set_ret(&attr, 300),
|
||||||
|
Some(_) => return sys_error(ENOENT),
|
||||||
|
None => return sys_error(EFAULT),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
|
||||||
|
let items = unsafe { hm.iter() }.collect::<Result<Vec<_>, _>>().unwrap();
|
||||||
|
assert_eq!(&items, &[(10, 100), (30, 300)])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iter_key_error() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
|
||||||
|
attr,
|
||||||
|
} => {
|
||||||
|
match bpf_key(&attr) {
|
||||||
|
None => set_next_key(&attr, 10),
|
||||||
|
Some(10) => set_next_key(&attr, 20),
|
||||||
|
Some(20) => return sys_error(EFAULT),
|
||||||
|
Some(30) => return sys_error(ENOENT),
|
||||||
|
Some(_) => panic!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
|
||||||
|
attr,
|
||||||
|
} => lookup_elem(&attr),
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
|
||||||
|
let mut iter = unsafe { hm.iter() };
|
||||||
|
assert!(matches!(iter.next(), Some(Ok((10, 100)))));
|
||||||
|
assert!(matches!(iter.next(), Some(Ok((20, 200)))));
|
||||||
|
assert!(matches!(iter.next(), Some(Err(MapError::GetNextKeyFailed { .. }))));
|
||||||
|
assert!(matches!(iter.next(), None));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iter_value_error() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_GET_NEXT_KEY,
|
||||||
|
attr,
|
||||||
|
} => get_next_key(&attr),
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_LOOKUP_ELEM,
|
||||||
|
attr,
|
||||||
|
} => {
|
||||||
|
match bpf_key(&attr) {
|
||||||
|
Some(10) => set_ret(&attr, 100),
|
||||||
|
Some(20) => return sys_error(EFAULT),
|
||||||
|
Some(30) => set_ret(&attr, 300),
|
||||||
|
Some(_) => return sys_error(ENOENT),
|
||||||
|
None => return sys_error(EFAULT),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
_ => sys_error(EFAULT),
|
||||||
|
});
|
||||||
|
let map = Map {
|
||||||
|
obj: new_obj_map("TEST"),
|
||||||
|
fd: Some(42),
|
||||||
|
};
|
||||||
|
let hm = HashMap::<_, u32, u32>::new(&map).unwrap();
|
||||||
|
|
||||||
|
let mut iter = unsafe { hm.iter() };
|
||||||
|
assert!(matches!(iter.next(), Some(Ok((10, 100)))));
|
||||||
|
assert!(matches!(iter.next(), Some(Err(MapError::LookupElementFailed { .. }))));
|
||||||
|
assert!(matches!(iter.next(), Some(Ok((30, 300)))));
|
||||||
|
assert!(matches!(iter.next(), None));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
use std::{ffi::CString, io};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{obj, syscalls::bpf_create_map, RawFd};
|
||||||
|
|
||||||
|
mod hash_map;
|
||||||
|
pub use hash_map::*;
|
||||||
|
|
||||||
|
mod perf_map;
|
||||||
|
pub use perf_map::*;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum MapError {
|
||||||
|
#[error("invalid map type {map_type}")]
|
||||||
|
InvalidMapType { map_type: u32 },
|
||||||
|
|
||||||
|
#[error("invalid map name `{name}`")]
|
||||||
|
InvalidName { name: String },
|
||||||
|
|
||||||
|
#[error("the map `{name}` has not been created")]
|
||||||
|
NotCreated { name: String },
|
||||||
|
|
||||||
|
#[error("the map `{name}` has already been created")]
|
||||||
|
AlreadyCreated { name: String },
|
||||||
|
|
||||||
|
#[error("failed to create map `{name}`: {code}")]
|
||||||
|
CreateFailed {
|
||||||
|
name: String,
|
||||||
|
code: i64,
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("invalid key size {size}, expected {expected}")]
|
||||||
|
InvalidKeySize { size: usize, expected: usize },
|
||||||
|
|
||||||
|
#[error("invalid value size {size}, expected {expected}")]
|
||||||
|
InvalidValueSize { size: usize, expected: usize },
|
||||||
|
|
||||||
|
#[error("the BPF_MAP_UPDATE_ELEM syscall failed with code {code} io_error {io_error}")]
|
||||||
|
UpdateElementFailed { code: i64, io_error: io::Error },
|
||||||
|
|
||||||
|
#[error("the BPF_MAP_LOOKUP_ELEM syscall failed with code {code} io_error {io_error}")]
|
||||||
|
LookupElementFailed { code: i64, io_error: io::Error },
|
||||||
|
|
||||||
|
#[error("the BPF_MAP_DELETE_ELEM syscall failed with code {code} io_error {io_error}")]
|
||||||
|
DeleteElementFailed { code: i64, io_error: io::Error },
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"the BPF_MAP_LOOKUP_AND_DELETE_ELEM syscall failed with code {code} io_error {io_error}"
|
||||||
|
)]
|
||||||
|
LookupAndDeleteElementFailed { code: i64, io_error: io::Error },
|
||||||
|
|
||||||
|
#[error("the BPF_MAP_GET_NEXT_KEY syscall failed with code {code} io_error {io_error}")]
|
||||||
|
GetNextKeyFailed { code: i64, io_error: io::Error },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Map {
|
||||||
|
pub(crate) obj: obj::Map,
|
||||||
|
pub(crate) fd: Option<RawFd>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Map {
|
||||||
|
pub fn create(&mut self) -> Result<RawFd, MapError> {
|
||||||
|
let name = self.obj.name.clone();
|
||||||
|
if self.fd.is_some() {
|
||||||
|
return Err(MapError::AlreadyCreated { name: name.clone() });
|
||||||
|
}
|
||||||
|
|
||||||
|
let c_name =
|
||||||
|
CString::new(name.clone()).map_err(|_| MapError::InvalidName { name: name.clone() })?;
|
||||||
|
|
||||||
|
let fd = bpf_create_map(&c_name, &self.obj.def).map_err(|(code, io_error)| {
|
||||||
|
MapError::CreateFailed {
|
||||||
|
name,
|
||||||
|
code,
|
||||||
|
io_error,
|
||||||
|
}
|
||||||
|
})? as RawFd;
|
||||||
|
|
||||||
|
self.fd = Some(fd);
|
||||||
|
|
||||||
|
Ok(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fd_or_err(&self) -> Result<RawFd, MapError> {
|
||||||
|
self.fd.ok_or_else(|| MapError::NotCreated {
|
||||||
|
name: self.obj.name.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use libc::EFAULT;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bpf_map_def,
|
||||||
|
generated::{bpf_cmd, bpf_map_type::BPF_MAP_TYPE_HASH},
|
||||||
|
syscalls::{override_syscall, Syscall},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn new_obj_map(name: &str) -> obj::Map {
|
||||||
|
obj::Map {
|
||||||
|
name: name.to_string(),
|
||||||
|
def: bpf_map_def {
|
||||||
|
map_type: BPF_MAP_TYPE_HASH,
|
||||||
|
key_size: 4,
|
||||||
|
value_size: 4,
|
||||||
|
max_entries: 1024,
|
||||||
|
map_flags: 0,
|
||||||
|
},
|
||||||
|
section_index: 0,
|
||||||
|
data: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_map(name: &str) -> Map {
|
||||||
|
Map {
|
||||||
|
obj: new_obj_map(name),
|
||||||
|
fd: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create() {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::Bpf {
|
||||||
|
cmd: bpf_cmd::BPF_MAP_CREATE,
|
||||||
|
..
|
||||||
|
} => Ok(42),
|
||||||
|
_ => Err((-1, io::Error::from_raw_os_error(EFAULT))),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut map = new_map("foo");
|
||||||
|
assert!(matches!(map.create(), Ok(42)));
|
||||||
|
assert_eq!(map.fd, Some(42));
|
||||||
|
assert!(matches!(map.create(), Err(MapError::AlreadyCreated { .. })));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_failed() {
|
||||||
|
override_syscall(|_| {
|
||||||
|
return Err((-42, io::Error::from_raw_os_error(EFAULT)));
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut map = new_map("foo");
|
||||||
|
let ret = map.create();
|
||||||
|
assert!(matches!(ret, Err(MapError::CreateFailed { .. })));
|
||||||
|
if let Err(MapError::CreateFailed {
|
||||||
|
name,
|
||||||
|
code,
|
||||||
|
io_error,
|
||||||
|
}) = ret
|
||||||
|
{
|
||||||
|
assert_eq!(name, "foo");
|
||||||
|
assert_eq!(code, -42);
|
||||||
|
assert_eq!(io_error.raw_os_error(), Some(EFAULT));
|
||||||
|
}
|
||||||
|
assert_eq!(map.fd, None);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,700 @@
|
|||||||
|
use std::{
|
||||||
|
convert::TryFrom,
|
||||||
|
ffi::c_void,
|
||||||
|
fs, io, mem, ptr, slice,
|
||||||
|
str::FromStr,
|
||||||
|
sync::atomic::{self, AtomicPtr, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use bytes::BytesMut;
|
||||||
|
use libc::{
|
||||||
|
c_int, close, munmap, sysconf, MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, _SC_PAGESIZE,
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
generated::{
|
||||||
|
bpf_map_type::BPF_MAP_TYPE_PERF_EVENT_ARRAY, perf_event_header, perf_event_mmap_page,
|
||||||
|
perf_event_type::*,
|
||||||
|
},
|
||||||
|
maps::{Map, MapError},
|
||||||
|
syscalls::{bpf_map_update_elem, perf_event_ioctl, perf_event_open},
|
||||||
|
RawFd, PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ONLINE_CPUS: &str = "/sys/devices/system/cpu/online";
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PerfBufferError {
|
||||||
|
#[error("invalid page count {page_count}, the value must be a power of two")]
|
||||||
|
InvalidPageCount { page_count: usize },
|
||||||
|
|
||||||
|
#[error("perf_event_open failed: {io_error}")]
|
||||||
|
OpenFailed {
|
||||||
|
#[source]
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("mmap failed: {io_error}")]
|
||||||
|
MMapFailed {
|
||||||
|
#[source]
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("PERF_EVENT_IOC_ENABLE failed: {io_error}")]
|
||||||
|
PerfEventEnableFailed {
|
||||||
|
#[source]
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("read_events() was called with no output buffers")]
|
||||||
|
NoBuffers,
|
||||||
|
|
||||||
|
#[error("the buffer needs to be of at least {size} bytes")]
|
||||||
|
MoreSpaceNeeded { size: usize },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Events {
|
||||||
|
pub read: usize,
|
||||||
|
pub lost: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PerfBuffer {
|
||||||
|
buf: AtomicPtr<perf_event_mmap_page>,
|
||||||
|
size: usize,
|
||||||
|
page_size: usize,
|
||||||
|
fd: RawFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PerfBuffer {
|
||||||
|
fn open(
|
||||||
|
cpu_id: u32,
|
||||||
|
page_size: usize,
|
||||||
|
page_count: usize,
|
||||||
|
) -> Result<PerfBuffer, PerfBufferError> {
|
||||||
|
if !page_count.is_power_of_two() {
|
||||||
|
return Err(PerfBufferError::InvalidPageCount { page_count });
|
||||||
|
}
|
||||||
|
|
||||||
|
let fd = perf_event_open(cpu_id as i32)
|
||||||
|
.map_err(|(_, io_error)| PerfBufferError::OpenFailed { io_error })?
|
||||||
|
as RawFd;
|
||||||
|
let size = page_size * page_count;
|
||||||
|
let buf = unsafe {
|
||||||
|
mmap(
|
||||||
|
ptr::null_mut(),
|
||||||
|
size + page_size,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
fd,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if buf == MAP_FAILED {
|
||||||
|
return Err(PerfBufferError::MMapFailed {
|
||||||
|
io_error: io::Error::last_os_error(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let perf_buf = PerfBuffer {
|
||||||
|
buf: AtomicPtr::new(buf as *mut perf_event_mmap_page),
|
||||||
|
fd,
|
||||||
|
size,
|
||||||
|
page_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
perf_event_ioctl(fd, PERF_EVENT_IOC_ENABLE, 0)
|
||||||
|
.map_err(|(_, io_error)| PerfBufferError::PerfEventEnableFailed { io_error })?;
|
||||||
|
|
||||||
|
Ok(perf_buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_events(&mut self, buffers: &mut [BytesMut]) -> Result<Events, PerfBufferError> {
|
||||||
|
if buffers.is_empty() {
|
||||||
|
return Err(PerfBufferError::NoBuffers);
|
||||||
|
}
|
||||||
|
let header = self.buf.load(Ordering::SeqCst);
|
||||||
|
let base = header as usize + self.page_size;
|
||||||
|
|
||||||
|
let mut events = Events { read: 0, lost: 0 };
|
||||||
|
let mut buf_n = 0;
|
||||||
|
|
||||||
|
let fill_buf = |start_off, base, mmap_size, out_buf: &mut [u8]| {
|
||||||
|
let len = out_buf.len();
|
||||||
|
|
||||||
|
let end = (start_off + len) % mmap_size;
|
||||||
|
let start = start_off % mmap_size;
|
||||||
|
|
||||||
|
if start < end {
|
||||||
|
out_buf.copy_from_slice(unsafe {
|
||||||
|
slice::from_raw_parts((base + start) as *const u8, len)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let size = mmap_size - start;
|
||||||
|
unsafe {
|
||||||
|
out_buf[..size]
|
||||||
|
.copy_from_slice(slice::from_raw_parts((base + start) as *const u8, size));
|
||||||
|
out_buf[size..]
|
||||||
|
.copy_from_slice(slice::from_raw_parts(base as *const u8, len - size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let read_event = |event_start, event_type, base, buf: &mut BytesMut| {
|
||||||
|
let sample_size = match event_type {
|
||||||
|
PERF_RECORD_SAMPLE | PERF_RECORD_LOST => {
|
||||||
|
let mut size = [0u8; mem::size_of::<u32>()];
|
||||||
|
fill_buf(
|
||||||
|
event_start + mem::size_of::<perf_event_header>(),
|
||||||
|
base,
|
||||||
|
self.size,
|
||||||
|
&mut size,
|
||||||
|
);
|
||||||
|
u32::from_ne_bytes(size)
|
||||||
|
}
|
||||||
|
_ => return Ok(None),
|
||||||
|
} as usize;
|
||||||
|
|
||||||
|
let sample_start =
|
||||||
|
(event_start + mem::size_of::<perf_event_header>() + mem::size_of::<u32>())
|
||||||
|
% self.size;
|
||||||
|
|
||||||
|
match event_type {
|
||||||
|
PERF_RECORD_SAMPLE => {
|
||||||
|
buf.clear();
|
||||||
|
if sample_size > buf.capacity() {
|
||||||
|
return Err(PerfBufferError::MoreSpaceNeeded { size: sample_size });
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { buf.set_len(sample_size) };
|
||||||
|
|
||||||
|
fill_buf(sample_start, base, self.size, buf);
|
||||||
|
|
||||||
|
Ok(Some((1, 0)))
|
||||||
|
}
|
||||||
|
PERF_RECORD_LOST => {
|
||||||
|
let mut count = [0u8; mem::size_of::<u64>()];
|
||||||
|
fill_buf(
|
||||||
|
event_start + mem::size_of::<perf_event_header>() + mem::size_of::<u64>(),
|
||||||
|
base,
|
||||||
|
self.size,
|
||||||
|
&mut count,
|
||||||
|
);
|
||||||
|
Ok(Some((0, u64::from_ne_bytes(count) as usize)))
|
||||||
|
}
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let head = unsafe { (*header).data_head } as usize;
|
||||||
|
let mut tail = unsafe { (*header).data_tail } as usize;
|
||||||
|
while head != tail {
|
||||||
|
if buf_n == buffers.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = &mut buffers[buf_n];
|
||||||
|
|
||||||
|
let event_start = tail % self.size;
|
||||||
|
let event =
|
||||||
|
unsafe { ptr::read_unaligned((base + event_start) as *const perf_event_header) };
|
||||||
|
let event_size = event.size as usize;
|
||||||
|
|
||||||
|
match read_event(event_start, event.type_, base, buf) {
|
||||||
|
Ok(Some((read, lost))) => {
|
||||||
|
if read > 0 {
|
||||||
|
buf_n += 1;
|
||||||
|
events.read += read;
|
||||||
|
}
|
||||||
|
events.lost += lost;
|
||||||
|
}
|
||||||
|
Ok(None) => { /* skip unknown event type */ }
|
||||||
|
Err(PerfBufferError::MoreSpaceNeeded { .. }) if events.read > 0 => {
|
||||||
|
// we have processed some events so we're going to return those. In the
|
||||||
|
// next read_events() we'll return an error unless the caller increases the
|
||||||
|
// buffer size
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// we got an error and we didn't process any events, propagate the error
|
||||||
|
// and give the caller a chance to increase buffers
|
||||||
|
atomic::fence(Ordering::SeqCst);
|
||||||
|
unsafe { (*header).data_tail = tail as u64 };
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tail += event_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic::fence(Ordering::SeqCst);
|
||||||
|
unsafe { (*header).data_tail = tail as u64 };
|
||||||
|
|
||||||
|
return Ok(events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for PerfBuffer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let _ = perf_event_ioctl(self.fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||||
|
munmap(
|
||||||
|
self.buf.load(Ordering::SeqCst) as *mut c_void,
|
||||||
|
self.size + self.page_size,
|
||||||
|
);
|
||||||
|
close(self.fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum PerfMapError {
|
||||||
|
#[error("error parsing /sys/devices/system/cpu/online")]
|
||||||
|
InvalidOnlineCpuFile,
|
||||||
|
|
||||||
|
#[error("no CPUs specified")]
|
||||||
|
NoCpus,
|
||||||
|
|
||||||
|
#[error("invalid cpu {cpu_id}")]
|
||||||
|
InvalidCpu { cpu_id: u32 },
|
||||||
|
|
||||||
|
#[error("map error: {map_error}")]
|
||||||
|
MapError {
|
||||||
|
#[from]
|
||||||
|
map_error: MapError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("perf buffer error: {buf_error}")]
|
||||||
|
PerfBufferError {
|
||||||
|
#[from]
|
||||||
|
buf_error: PerfBufferError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("bpf_map_update_elem failed: {io_error}")]
|
||||||
|
UpdateElementFailed {
|
||||||
|
#[source]
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PerfMap<'map> {
|
||||||
|
map: &'map Map,
|
||||||
|
cpu_fds: Vec<(u32, RawFd)>,
|
||||||
|
buffers: Vec<Option<PerfBuffer>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'map> PerfMap<'map> {
|
||||||
|
pub fn new(
|
||||||
|
map: &'map Map,
|
||||||
|
cpu_ids: Option<Vec<u32>>,
|
||||||
|
page_count: Option<usize>,
|
||||||
|
) -> Result<PerfMap<'map>, PerfMapError> {
|
||||||
|
let map_type = map.obj.def.map_type;
|
||||||
|
if map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY {
|
||||||
|
return Err(MapError::InvalidMapType {
|
||||||
|
map_type: map_type as u32,
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cpu_ids = match cpu_ids {
|
||||||
|
Some(ids) => ids,
|
||||||
|
None => online_cpus().map_err(|_| PerfMapError::InvalidOnlineCpuFile)?,
|
||||||
|
};
|
||||||
|
if cpu_ids.is_empty() {
|
||||||
|
return Err(PerfMapError::NoCpus);
|
||||||
|
}
|
||||||
|
cpu_ids.sort();
|
||||||
|
let min_cpu = cpu_ids.first().unwrap();
|
||||||
|
let max_cpu = cpu_ids.last().unwrap();
|
||||||
|
let mut buffers = (*min_cpu..=*max_cpu).map(|_| None).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let map_fd = map.fd_or_err()?;
|
||||||
|
let page_size = unsafe { sysconf(_SC_PAGESIZE) } as usize;
|
||||||
|
|
||||||
|
let mut cpu_fds = Vec::new();
|
||||||
|
for cpu_id in &cpu_ids {
|
||||||
|
let buf = PerfBuffer::open(*cpu_id, page_size, page_count.unwrap_or(2))?;
|
||||||
|
bpf_map_update_elem(map_fd, cpu_id, &buf.fd, 0)
|
||||||
|
.map_err(|(_, io_error)| PerfMapError::UpdateElementFailed { io_error })?;
|
||||||
|
cpu_fds.push((*cpu_id, buf.fd));
|
||||||
|
buffers[*cpu_id as usize] = Some(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PerfMap {
|
||||||
|
map,
|
||||||
|
cpu_fds,
|
||||||
|
buffers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_file_descriptors(&self) -> &[(u32, RawFd)] {
|
||||||
|
self.cpu_fds.as_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_cpu_events(
|
||||||
|
&mut self,
|
||||||
|
cpu_id: u32,
|
||||||
|
buffers: &mut [BytesMut],
|
||||||
|
) -> Result<Events, PerfMapError> {
|
||||||
|
let buf = match self.buffers.get_mut(cpu_id as usize) {
|
||||||
|
None | Some(None) => return Err(PerfMapError::InvalidCpu { cpu_id }),
|
||||||
|
Some(Some(buf)) => buf,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(buf.read_events(buffers)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'inner> TryFrom<&'inner Map> for PerfMap<'inner> {
|
||||||
|
type Error = PerfMapError;
|
||||||
|
|
||||||
|
fn try_from(inner: &'inner Map) -> Result<PerfMap<'inner>, PerfMapError> {
|
||||||
|
PerfMap::new(inner, None, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn online_cpus() -> Result<Vec<u32>, ()> {
|
||||||
|
let data = fs::read_to_string(ONLINE_CPUS).map_err(|_| ())?;
|
||||||
|
parse_online_cpus(data.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_online_cpus(data: &str) -> Result<Vec<u32>, ()> {
|
||||||
|
let mut cpus = Vec::new();
|
||||||
|
for range in data.split(',') {
|
||||||
|
cpus.extend({
|
||||||
|
match range
|
||||||
|
.splitn(2, '-')
|
||||||
|
.map(u32::from_str)
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.map_err(|_| ())?
|
||||||
|
.as_slice()
|
||||||
|
{
|
||||||
|
&[] | &[_, _, _, ..] => return Err(()),
|
||||||
|
&[start] => start..=start,
|
||||||
|
&[start, end] => start..=end,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cpus)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, allow(unused_variables))]
|
||||||
|
unsafe fn mmap(
|
||||||
|
addr: *mut c_void,
|
||||||
|
len: usize,
|
||||||
|
prot: c_int,
|
||||||
|
flags: c_int,
|
||||||
|
fd: i32,
|
||||||
|
offset: i64,
|
||||||
|
) -> *mut c_void {
|
||||||
|
#[cfg(not(test))]
|
||||||
|
return libc::mmap(addr, len, prot, flags, fd, offset);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::syscalls::TEST_MMAP_RET;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
TEST_MMAP_RET.with(|ret| *ret.borrow())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Sample {
|
||||||
|
header: perf_event_header,
|
||||||
|
pub size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LostSamples {
|
||||||
|
header: perf_event_header,
|
||||||
|
pub id: u64,
|
||||||
|
pub count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{
|
||||||
|
generated::perf_event_mmap_page,
|
||||||
|
syscalls::{override_syscall, Syscall, TEST_MMAP_RET},
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::{convert::TryInto, fmt::Debug, iter::FromIterator, mem};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_online_cpus() {
|
||||||
|
assert_eq!(parse_online_cpus("0").unwrap(), vec![0]);
|
||||||
|
assert_eq!(parse_online_cpus("0,1").unwrap(), vec![0, 1]);
|
||||||
|
assert_eq!(parse_online_cpus("0,1,2").unwrap(), vec![0, 1, 2]);
|
||||||
|
assert_eq!(parse_online_cpus("0-7").unwrap(), Vec::from_iter(0..=7));
|
||||||
|
assert_eq!(parse_online_cpus("0-3,4-7").unwrap(), Vec::from_iter(0..=7));
|
||||||
|
assert_eq!(parse_online_cpus("0-5,6,7").unwrap(), Vec::from_iter(0..=7));
|
||||||
|
assert!(parse_online_cpus("").is_err());
|
||||||
|
assert!(parse_online_cpus("0-1,2-").is_err());
|
||||||
|
assert!(parse_online_cpus("foo").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
union MMappedBuf {
|
||||||
|
mmap_page: perf_event_mmap_page,
|
||||||
|
data: [u8; PAGE_SIZE * 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fake_mmap(buf: &mut MMappedBuf) {
|
||||||
|
override_syscall(|call| match call {
|
||||||
|
Syscall::PerfEventOpen { .. } | Syscall::PerfEventIoctl { .. } => Ok(42),
|
||||||
|
_ => panic!(),
|
||||||
|
});
|
||||||
|
TEST_MMAP_RET.with(|ret| *ret.borrow_mut() = buf as *const _ as *mut _);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_page_count() {
|
||||||
|
assert!(matches!(
|
||||||
|
PerfBuffer::open(1, PAGE_SIZE, 0),
|
||||||
|
Err(PerfBufferError::InvalidPageCount { .. })
|
||||||
|
));
|
||||||
|
assert!(matches!(
|
||||||
|
PerfBuffer::open(1, PAGE_SIZE, 3),
|
||||||
|
Err(PerfBufferError::InvalidPageCount { .. })
|
||||||
|
));
|
||||||
|
assert!(matches!(
|
||||||
|
PerfBuffer::open(1, PAGE_SIZE, 5),
|
||||||
|
Err(PerfBufferError::InvalidPageCount { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_out_bufs() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
assert!(matches!(
|
||||||
|
buf.read_events(&mut []),
|
||||||
|
Err(PerfBufferError::NoBuffers)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_events() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
let out_buf = BytesMut::with_capacity(4);
|
||||||
|
assert_eq!(
|
||||||
|
buf.read_events(&mut [out_buf]).unwrap(),
|
||||||
|
Events { read: 0, lost: 0 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_first_lost() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
|
||||||
|
let evt = LostSamples {
|
||||||
|
header: perf_event_header {
|
||||||
|
type_: PERF_RECORD_LOST,
|
||||||
|
misc: 0,
|
||||||
|
size: mem::size_of::<LostSamples>() as u16,
|
||||||
|
},
|
||||||
|
id: 1,
|
||||||
|
count: 0xCAFEBABE,
|
||||||
|
};
|
||||||
|
write(&mut mmapped_buf, 0, evt);
|
||||||
|
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
let out_buf = BytesMut::with_capacity(0);
|
||||||
|
let events = buf.read_events(&mut [out_buf]).unwrap();
|
||||||
|
assert_eq!(events.lost, 0xCAFEBABE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PerfSample<T: Debug> {
|
||||||
|
s_hdr: Sample,
|
||||||
|
value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<T: Debug>(mmapped_buf: &mut MMappedBuf, offset: usize, value: T) -> usize {
|
||||||
|
let dst = (mmapped_buf as *const _ as usize + PAGE_SIZE + offset) as *const PerfSample<T>
|
||||||
|
as *mut T;
|
||||||
|
unsafe {
|
||||||
|
ptr::write_unaligned(dst, value);
|
||||||
|
mmapped_buf.mmap_page.data_head = (offset + mem::size_of::<T>()) as u64;
|
||||||
|
mmapped_buf.mmap_page.data_head as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_sample<T: Debug>(mmapped_buf: &mut MMappedBuf, offset: usize, value: T) -> usize {
|
||||||
|
let sample = PerfSample {
|
||||||
|
s_hdr: Sample {
|
||||||
|
header: perf_event_header {
|
||||||
|
type_: PERF_RECORD_SAMPLE,
|
||||||
|
misc: 0,
|
||||||
|
size: mem::size_of::<PerfSample<T>>() as u16,
|
||||||
|
},
|
||||||
|
size: mem::size_of::<T>() as u32,
|
||||||
|
},
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
write(mmapped_buf, offset, sample)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn u32_from_buf(buf: &[u8]) -> u32 {
|
||||||
|
u32::from_ne_bytes(buf[..4].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn u64_from_buf(buf: &[u8]) -> u64 {
|
||||||
|
u64::from_ne_bytes(buf[..8].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_first_sample() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
|
||||||
|
write_sample(&mut mmapped_buf, 0, 0xCAFEBABEu32);
|
||||||
|
|
||||||
|
let mut out_bufs = [BytesMut::with_capacity(4)];
|
||||||
|
|
||||||
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
||||||
|
assert_eq!(events, Events { lost: 0, read: 1 });
|
||||||
|
assert_eq!(u32_from_buf(&out_bufs[0]), 0xCAFEBABE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_many_with_many_reads() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
|
||||||
|
let next = write_sample(&mut mmapped_buf, 0, 0xCAFEBABEu32);
|
||||||
|
write_sample(&mut mmapped_buf, next, 0xBADCAFEu32);
|
||||||
|
|
||||||
|
let mut out_bufs = [BytesMut::with_capacity(4)];
|
||||||
|
|
||||||
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
||||||
|
assert_eq!(events, Events { lost: 0, read: 1 });
|
||||||
|
assert_eq!(u32_from_buf(&out_bufs[0]), 0xCAFEBABE);
|
||||||
|
|
||||||
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
||||||
|
assert_eq!(events, Events { lost: 0, read: 1 });
|
||||||
|
assert_eq!(u32_from_buf(&out_bufs[0]), 0xBADCAFE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_many_with_one_read() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
|
||||||
|
let next = write_sample(&mut mmapped_buf, 0, 0xCAFEBABEu32);
|
||||||
|
write_sample(&mut mmapped_buf, next, 0xBADCAFEu32);
|
||||||
|
|
||||||
|
let mut out_bufs = (0..3)
|
||||||
|
.map(|_| BytesMut::with_capacity(4))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
||||||
|
assert_eq!(events, Events { lost: 0, read: 2 });
|
||||||
|
assert_eq!(u32_from_buf(&out_bufs[0]), 0xCAFEBABE);
|
||||||
|
assert_eq!(u32_from_buf(&out_bufs[1]), 0xBADCAFE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_last_sample() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
|
||||||
|
let offset = PAGE_SIZE - mem::size_of::<PerfSample<u32>>();
|
||||||
|
mmapped_buf.mmap_page.data_tail = offset as u64;
|
||||||
|
write_sample(&mut mmapped_buf, offset, 0xCAFEBABEu32);
|
||||||
|
|
||||||
|
let mut out_bufs = [BytesMut::with_capacity(4)];
|
||||||
|
|
||||||
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
||||||
|
assert_eq!(events, Events { lost: 0, read: 1 });
|
||||||
|
assert_eq!(u32_from_buf(&out_bufs[0]), 0xCAFEBABE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_wrapping_sample_size() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
|
||||||
|
let header = perf_event_header {
|
||||||
|
type_: PERF_RECORD_SAMPLE,
|
||||||
|
misc: 0,
|
||||||
|
size: mem::size_of::<PerfSample<u64>>() as u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset = PAGE_SIZE - mem::size_of::<perf_event_header>() - 2;
|
||||||
|
mmapped_buf.mmap_page.data_tail = offset as u64;
|
||||||
|
write(&mut mmapped_buf, offset, header);
|
||||||
|
write(&mut mmapped_buf, PAGE_SIZE - 2, 0x0004u16);
|
||||||
|
write(&mut mmapped_buf, 0, 0x0000u16);
|
||||||
|
write(&mut mmapped_buf, 2, 0xBAADCAFEu32);
|
||||||
|
|
||||||
|
let mut out_bufs = [BytesMut::with_capacity(8)];
|
||||||
|
|
||||||
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
||||||
|
assert_eq!(events, Events { lost: 0, read: 1 });
|
||||||
|
assert_eq!(u32_from_buf(&out_bufs[0]), 0xBAADCAFE);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_wrapping_value() {
|
||||||
|
let mut mmapped_buf = MMappedBuf {
|
||||||
|
data: [0; PAGE_SIZE * 2],
|
||||||
|
};
|
||||||
|
fake_mmap(&mut mmapped_buf);
|
||||||
|
let mut buf = PerfBuffer::open(1, PAGE_SIZE, 1).unwrap();
|
||||||
|
|
||||||
|
let sample = PerfSample {
|
||||||
|
s_hdr: Sample {
|
||||||
|
header: perf_event_header {
|
||||||
|
type_: PERF_RECORD_SAMPLE,
|
||||||
|
misc: 0,
|
||||||
|
size: mem::size_of::<PerfSample<u64>>() as u16,
|
||||||
|
},
|
||||||
|
size: mem::size_of::<u64>() as u32,
|
||||||
|
},
|
||||||
|
value: 0xCAFEBABEu32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset = PAGE_SIZE - mem::size_of::<PerfSample<u32>>();
|
||||||
|
mmapped_buf.mmap_page.data_tail = offset as u64;
|
||||||
|
write(&mut mmapped_buf, offset, sample);
|
||||||
|
write(&mut mmapped_buf, 0, 0xBAADCAFEu32);
|
||||||
|
|
||||||
|
let mut out_bufs = [BytesMut::with_capacity(8)];
|
||||||
|
|
||||||
|
let events = buf.read_events(&mut out_bufs).unwrap();
|
||||||
|
assert_eq!(events, Events { lost: 0, read: 1 });
|
||||||
|
assert_eq!(u64_from_buf(&out_bufs[0]), 0xBAADCAFECAFEBABE);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,414 @@
|
|||||||
|
mod relocation;
|
||||||
|
|
||||||
|
use object::{
|
||||||
|
pod,
|
||||||
|
read::{Object as ElfObject, ObjectSection, Section},
|
||||||
|
Endianness, ObjectSymbol, ObjectSymbolTable, SectionIndex, SymbolIndex,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
ffi::{CStr, CString},
|
||||||
|
mem,
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub use self::relocation::{relocate, RelocationError};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bpf_map_def,
|
||||||
|
generated::{bpf_insn, bpf_map_type::BPF_MAP_TYPE_ARRAY},
|
||||||
|
obj::relocation::{Relocation, Symbol},
|
||||||
|
};
|
||||||
|
|
||||||
|
const KERNEL_VERSION_ANY: u32 = 0xFFFF_FFFE;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Object {
|
||||||
|
pub(crate) endianness: Endianness,
|
||||||
|
pub license: CString,
|
||||||
|
pub kernel_version: KernelVersion,
|
||||||
|
pub(crate) maps: HashMap<String, Map>,
|
||||||
|
pub(crate) programs: HashMap<String, Program>,
|
||||||
|
pub(crate) relocations: HashMap<SectionIndex, Vec<Relocation>>,
|
||||||
|
pub(crate) symbol_table: HashMap<SymbolIndex, Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Map {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) def: bpf_map_def,
|
||||||
|
pub(crate) section_index: usize,
|
||||||
|
pub(crate) data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct Program {
|
||||||
|
pub(crate) license: CString,
|
||||||
|
pub(crate) kernel_version: KernelVersion,
|
||||||
|
pub(crate) instructions: Vec<bpf_insn>,
|
||||||
|
pub(crate) kind: ProgramKind,
|
||||||
|
pub(crate) section_index: SectionIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum ProgramKind {
|
||||||
|
KProbe,
|
||||||
|
UProbe,
|
||||||
|
Xdp,
|
||||||
|
TracePoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ProgramKind {
|
||||||
|
type Err = ParseError;
|
||||||
|
|
||||||
|
fn from_str(kind: &str) -> Result<ProgramKind, ParseError> {
|
||||||
|
use ProgramKind::*;
|
||||||
|
Ok(match kind {
|
||||||
|
"kprobe" => KProbe,
|
||||||
|
"uprobe" => UProbe,
|
||||||
|
"xdp" => Xdp,
|
||||||
|
"trace_point" => TracePoint,
|
||||||
|
_ => {
|
||||||
|
return Err(ParseError::InvalidProgramKind {
|
||||||
|
kind: kind.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object {
|
||||||
|
pub(crate) fn parse(data: &[u8]) -> Result<Object, ParseError> {
|
||||||
|
let obj = object::read::File::parse(data).map_err(|source| ParseError::Error { source })?;
|
||||||
|
let endianness = obj.endianness();
|
||||||
|
|
||||||
|
let section = obj
|
||||||
|
.section_by_name("license")
|
||||||
|
.ok_or(ParseError::MissingLicense)?;
|
||||||
|
let license = parse_license(BPFSection::try_from(§ion)?.data)?;
|
||||||
|
|
||||||
|
let section = obj
|
||||||
|
.section_by_name("version")
|
||||||
|
.ok_or(ParseError::MissingKernelVersion)?;
|
||||||
|
let kernel_version = parse_version(BPFSection::try_from(§ion)?.data, endianness)?;
|
||||||
|
|
||||||
|
let mut bpf_obj = Object {
|
||||||
|
endianness: endianness.into(),
|
||||||
|
license,
|
||||||
|
kernel_version,
|
||||||
|
maps: HashMap::new(),
|
||||||
|
programs: HashMap::new(),
|
||||||
|
relocations: HashMap::new(),
|
||||||
|
symbol_table: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for s in obj.sections() {
|
||||||
|
parse_section(&mut bpf_obj, BPFSection::try_from(&s)?)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(symbol_table) = obj.symbol_table() {
|
||||||
|
for symbol in symbol_table.symbols() {
|
||||||
|
bpf_obj.symbol_table.insert(
|
||||||
|
symbol.index(),
|
||||||
|
Symbol {
|
||||||
|
name: symbol.name().ok().map(String::from),
|
||||||
|
section_index: symbol.section().index(),
|
||||||
|
address: symbol.address(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(bpf_obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Error)]
|
||||||
|
pub enum ParseError {
|
||||||
|
#[error("error parsing ELF data")]
|
||||||
|
Error {
|
||||||
|
#[source]
|
||||||
|
source: object::read::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("no license specified")]
|
||||||
|
MissingLicense,
|
||||||
|
|
||||||
|
#[error("invalid license `{data:?}`: missing NULL terminator")]
|
||||||
|
MissingLicenseNullTerminator { data: Vec<u8> },
|
||||||
|
|
||||||
|
#[error("invalid license `{data:?}`")]
|
||||||
|
InvalidLicense { data: Vec<u8> },
|
||||||
|
|
||||||
|
#[error("missing kernel version")]
|
||||||
|
MissingKernelVersion,
|
||||||
|
|
||||||
|
#[error("invalid kernel version `{data:?}`")]
|
||||||
|
InvalidKernelVersion { data: Vec<u8> },
|
||||||
|
|
||||||
|
#[error("error parsing section with index {index}")]
|
||||||
|
SectionError {
|
||||||
|
index: usize,
|
||||||
|
#[source]
|
||||||
|
source: object::read::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("unsupported relocation")]
|
||||||
|
UnsupportedRelocationKind,
|
||||||
|
|
||||||
|
#[error("invalid program kind `{kind}`")]
|
||||||
|
InvalidProgramKind { kind: String },
|
||||||
|
|
||||||
|
#[error("error parsing program `{name}`")]
|
||||||
|
InvalidProgramCode { name: String },
|
||||||
|
|
||||||
|
#[error("error parsing map `{name}`")]
|
||||||
|
InvalidMapDefinition { name: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BPFSection<'s> {
|
||||||
|
index: SectionIndex,
|
||||||
|
name: &'s str,
|
||||||
|
data: &'s [u8],
|
||||||
|
relocations: Vec<Relocation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'data, 'file, 's> TryFrom<&'s Section<'data, 'file>> for BPFSection<'s> {
|
||||||
|
type Error = ParseError;
|
||||||
|
|
||||||
|
fn try_from(section: &'s Section) -> Result<BPFSection<'s>, ParseError> {
|
||||||
|
let index = section.index();
|
||||||
|
let map_err = |source| ParseError::SectionError {
|
||||||
|
index: index.0,
|
||||||
|
source,
|
||||||
|
};
|
||||||
|
Ok(BPFSection {
|
||||||
|
index,
|
||||||
|
name: section.name().map_err(map_err)?,
|
||||||
|
data: section.data().map_err(map_err)?,
|
||||||
|
relocations: section
|
||||||
|
.relocations()
|
||||||
|
.map(|(offset, r)| {
|
||||||
|
Ok(Relocation {
|
||||||
|
kind: r.kind(),
|
||||||
|
target: r.target(),
|
||||||
|
addend: r.addend(),
|
||||||
|
offset,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_license(data: &[u8]) -> Result<CString, ParseError> {
|
||||||
|
if data.len() < 2 {
|
||||||
|
return Err(ParseError::InvalidLicense {
|
||||||
|
data: data.to_vec(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if data[data.len() - 1] != 0 {
|
||||||
|
return Err(ParseError::MissingLicenseNullTerminator {
|
||||||
|
data: data.to_vec(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(CStr::from_bytes_with_nul(data)
|
||||||
|
.map_err(|_| ParseError::InvalidLicense {
|
||||||
|
data: data.to_vec(),
|
||||||
|
})?
|
||||||
|
.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_version(data: &[u8], endianness: object::Endianness) -> Result<KernelVersion, ParseError> {
|
||||||
|
let data = match data.len() {
|
||||||
|
4 => data.try_into().unwrap(),
|
||||||
|
_ => {
|
||||||
|
return Err(ParseError::InvalidKernelVersion {
|
||||||
|
data: data.to_vec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let v = match endianness {
|
||||||
|
object::Endianness::Big => u32::from_be_bytes(data),
|
||||||
|
object::Endianness::Little => u32::from_le_bytes(data),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(match v {
|
||||||
|
KERNEL_VERSION_ANY => KernelVersion::Any,
|
||||||
|
v => KernelVersion::Version(v),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum KernelVersion {
|
||||||
|
Version(u32),
|
||||||
|
Any,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<KernelVersion> for u32 {
|
||||||
|
fn from(version: KernelVersion) -> u32 {
|
||||||
|
match version {
|
||||||
|
KernelVersion::Any => KERNEL_VERSION_ANY,
|
||||||
|
KernelVersion::Version(v) => v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_map(section: &BPFSection, name: &str) -> Result<Map, ParseError> {
|
||||||
|
let (def, data) = if name == ".bss" || name.starts_with(".data") || name.starts_with(".rodata")
|
||||||
|
{
|
||||||
|
let def = bpf_map_def {
|
||||||
|
map_type: BPF_MAP_TYPE_ARRAY,
|
||||||
|
key_size: mem::size_of::<u32>() as u32,
|
||||||
|
value_size: section.data.len() as u32,
|
||||||
|
max_entries: 1,
|
||||||
|
map_flags: 0, /* FIXME: set rodata readonly */
|
||||||
|
};
|
||||||
|
(def, section.data.to_vec())
|
||||||
|
} else {
|
||||||
|
(parse_map_def(name, section.data)?, Vec::new())
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Map {
|
||||||
|
section_index: section.index.0,
|
||||||
|
name: name.to_string(),
|
||||||
|
def,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_map_def(name: &str, data: &[u8]) -> Result<bpf_map_def, ParseError> {
|
||||||
|
let (def, rest) =
|
||||||
|
pod::from_bytes::<bpf_map_def>(data).map_err(|_| ParseError::InvalidMapDefinition {
|
||||||
|
name: name.to_string(),
|
||||||
|
})?;
|
||||||
|
if !rest.is_empty() {
|
||||||
|
return Err(ParseError::InvalidMapDefinition {
|
||||||
|
name: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(*def)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_program(bpf: &Object, section: &BPFSection, ty: &str) -> Result<Program, ParseError> {
|
||||||
|
let (code, rest) = pod::slice_from_bytes::<bpf_insn>(
|
||||||
|
section.data,
|
||||||
|
section.data.len() / mem::size_of::<bpf_insn>(),
|
||||||
|
)
|
||||||
|
.map_err(|_| ParseError::InvalidProgramCode {
|
||||||
|
name: section.name.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !rest.is_empty() {
|
||||||
|
return Err(ParseError::InvalidProgramCode {
|
||||||
|
name: section.name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Program {
|
||||||
|
section_index: section.index,
|
||||||
|
license: bpf.license.clone(),
|
||||||
|
kernel_version: bpf.kernel_version,
|
||||||
|
instructions: code.to_vec(),
|
||||||
|
kind: ProgramKind::from_str(ty)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_section(bpf: &mut Object, section: BPFSection) -> Result<(), ParseError> {
|
||||||
|
let parts = section.name.split("/").collect::<Vec<_>>();
|
||||||
|
|
||||||
|
match parts.as_slice() {
|
||||||
|
&[name] if name == ".bss" || name.starts_with(".data") || name.starts_with(".rodata") => {
|
||||||
|
bpf.maps
|
||||||
|
.insert(name.to_string(), parse_map(§ion, name)?);
|
||||||
|
}
|
||||||
|
&["maps", name] => {
|
||||||
|
bpf.maps
|
||||||
|
.insert(name.to_string(), parse_map(§ion, name)?);
|
||||||
|
}
|
||||||
|
&[ty @ "kprobe", name]
|
||||||
|
| &[ty @ "uprobe", name]
|
||||||
|
| &[ty @ "xdp", name]
|
||||||
|
| &[ty @ "trace_point", name] => {
|
||||||
|
bpf.programs
|
||||||
|
.insert(name.to_string(), parse_program(bpf, §ion, ty)?);
|
||||||
|
if !section.relocations.is_empty() {
|
||||||
|
bpf.relocations.insert(section.index, section.relocations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use object::Endianness;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_generic_error() {
|
||||||
|
assert!(matches!(
|
||||||
|
Object::parse(&b"foo"[..]),
|
||||||
|
Err(ParseError::Error { .. })
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_license() {
|
||||||
|
assert!(matches!(
|
||||||
|
parse_license(b""),
|
||||||
|
Err(ParseError::InvalidLicense { .. })
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
parse_license(b"\0"),
|
||||||
|
Err(ParseError::InvalidLicense { .. })
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
parse_license(b"GPL"),
|
||||||
|
Err(ParseError::MissingLicenseNullTerminator { .. })
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(parse_license(b"GPL\0").unwrap().to_str().unwrap(), "GPL");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_version() {
|
||||||
|
assert!(matches!(
|
||||||
|
parse_version(b"", Endianness::Little),
|
||||||
|
Err(ParseError::InvalidKernelVersion { .. })
|
||||||
|
));
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
parse_version(b"123", Endianness::Little),
|
||||||
|
Err(ParseError::InvalidKernelVersion { .. })
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse_version(&0xFFFF_FFFEu32.to_le_bytes(), Endianness::Little)
|
||||||
|
.expect("failed to parse magic version"),
|
||||||
|
KernelVersion::Any
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse_version(&0xFFFF_FFFEu32.to_be_bytes(), Endianness::Big)
|
||||||
|
.expect("failed to parse magic version"),
|
||||||
|
KernelVersion::Any
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse_version(&1234u32.to_le_bytes(), Endianness::Little)
|
||||||
|
.expect("failed to parse magic version"),
|
||||||
|
KernelVersion::Version(1234)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use object::{RelocationKind, RelocationTarget, SectionIndex};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use super::Object;
|
||||||
|
use crate::{
|
||||||
|
generated::{bpf_insn, BPF_PSEUDO_MAP_FD, BPF_PSEUDO_MAP_VALUE},
|
||||||
|
maps::Map,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Error)]
|
||||||
|
pub enum RelocationError {
|
||||||
|
#[error("unknown symbol, index `{index}`")]
|
||||||
|
UnknownSymbol { index: usize },
|
||||||
|
|
||||||
|
#[error("unknown symbol section, index `{index}`")]
|
||||||
|
UnknownSymbolSection { index: usize },
|
||||||
|
|
||||||
|
#[error("section `{section_index}` not found, referenced by symbol `{}`",
|
||||||
|
.symbol_name.clone().unwrap_or_else(|| .symbol_index.to_string()))]
|
||||||
|
RelocationSectionNotFound {
|
||||||
|
section_index: usize,
|
||||||
|
symbol_index: usize,
|
||||||
|
symbol_name: Option<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("the map `{name}` at section `{section_index}` has not been created")]
|
||||||
|
MapNotCreated { section_index: usize, name: String },
|
||||||
|
|
||||||
|
#[error("invalid relocation offset `{offset}`")]
|
||||||
|
InvalidRelocationOffset { offset: u64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub(crate) struct Relocation {
|
||||||
|
pub(crate) kind: RelocationKind,
|
||||||
|
pub(crate) target: RelocationTarget,
|
||||||
|
pub(crate) offset: u64,
|
||||||
|
pub(crate) addend: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct Symbol {
|
||||||
|
pub(crate) section_index: Option<SectionIndex>,
|
||||||
|
pub(crate) name: Option<String>,
|
||||||
|
pub(crate) address: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn relocate(obj: &mut Object, maps: &[Map]) -> Result<(), RelocationError> {
|
||||||
|
let maps_by_section = maps
|
||||||
|
.iter()
|
||||||
|
.map(|map| (map.obj.section_index, map))
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
|
for program in obj.programs.values_mut() {
|
||||||
|
if let Some(relocations) = obj.relocations.get(&program.section_index) {
|
||||||
|
for rel in relocations {
|
||||||
|
match rel.target {
|
||||||
|
RelocationTarget::Symbol(index) => {
|
||||||
|
let sym = obj
|
||||||
|
.symbol_table
|
||||||
|
.get(&index)
|
||||||
|
.ok_or(RelocationError::UnknownSymbol { index: index.0 })?;
|
||||||
|
|
||||||
|
let section_index = sym
|
||||||
|
.section_index
|
||||||
|
.ok_or(RelocationError::UnknownSymbolSection { index: index.0 })?;
|
||||||
|
|
||||||
|
let map = maps_by_section.get(§ion_index.0).ok_or(
|
||||||
|
RelocationError::RelocationSectionNotFound {
|
||||||
|
symbol_index: index.0,
|
||||||
|
symbol_name: sym.name.clone(),
|
||||||
|
section_index: section_index.0,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let map_fd = map.fd.ok_or_else(|| RelocationError::MapNotCreated {
|
||||||
|
name: map.obj.name.clone(),
|
||||||
|
section_index: section_index.0,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let instructions = &mut program.instructions;
|
||||||
|
let ins_index =
|
||||||
|
(rel.offset / std::mem::size_of::<bpf_insn>() as u64) as usize;
|
||||||
|
if ins_index >= instructions.len() {
|
||||||
|
return Err(RelocationError::InvalidRelocationOffset {
|
||||||
|
offset: rel.offset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if !map.obj.data.is_empty() {
|
||||||
|
instructions[ins_index].set_src_reg(BPF_PSEUDO_MAP_VALUE as u8);
|
||||||
|
instructions[ins_index + 1].imm =
|
||||||
|
instructions[ins_index].imm + sym.address as i32;
|
||||||
|
} else {
|
||||||
|
instructions[ins_index].set_src_reg(BPF_PSEUDO_MAP_FD as u8);
|
||||||
|
}
|
||||||
|
instructions[ins_index].imm = map_fd;
|
||||||
|
}
|
||||||
|
RelocationTarget::Section(_index) => {}
|
||||||
|
RelocationTarget::Absolute => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,262 @@
|
|||||||
|
mod perf_attach;
|
||||||
|
mod probe;
|
||||||
|
mod socket_filter;
|
||||||
|
mod trace_point;
|
||||||
|
mod xdp;
|
||||||
|
|
||||||
|
use libc::ENOSPC;
|
||||||
|
use perf_attach::*;
|
||||||
|
pub use probe::*;
|
||||||
|
pub use socket_filter::*;
|
||||||
|
pub use trace_point::*;
|
||||||
|
pub use xdp::*;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
cmp,
|
||||||
|
ffi::CStr,
|
||||||
|
io,
|
||||||
|
os::raw::c_uint,
|
||||||
|
path::PathBuf,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{obj, syscalls::bpf_load_program, RawFd};
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ProgramError {
|
||||||
|
#[error("the program {program} is already loaded")]
|
||||||
|
AlreadyLoaded { program: String },
|
||||||
|
|
||||||
|
#[error("the program {program} is not loaded")]
|
||||||
|
NotLoaded { program: String },
|
||||||
|
|
||||||
|
#[error("the BPF_PROG_LOAD syscall for `{program}` failed: {io_error}\nVerifier output:\n{verifier_log}")]
|
||||||
|
LoadFailed {
|
||||||
|
program: String,
|
||||||
|
io_error: io::Error,
|
||||||
|
verifier_log: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("FIXME")]
|
||||||
|
AlreadyDetached,
|
||||||
|
|
||||||
|
#[error("the perf_event_open syscall failed: {io_error}")]
|
||||||
|
PerfEventOpenFailed { io_error: io::Error },
|
||||||
|
|
||||||
|
#[error("PERF_EVENT_IOC_SET_BPF/PERF_EVENT_IOC_ENABLE failed: {io_error}")]
|
||||||
|
PerfEventAttachFailed { io_error: io::Error },
|
||||||
|
|
||||||
|
#[error("the program {program} is not attached")]
|
||||||
|
NotAttached { program: String },
|
||||||
|
|
||||||
|
#[error("error attaching {program}: BPF_LINK_CREATE failed with {io_error}")]
|
||||||
|
BpfLinkCreateFailed {
|
||||||
|
program: String,
|
||||||
|
#[source]
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("unkown network interface {name}")]
|
||||||
|
UnkownInterface { name: String },
|
||||||
|
|
||||||
|
#[error("error reading ld.so.cache file")]
|
||||||
|
InvalidLdSoCache { error_kind: io::ErrorKind },
|
||||||
|
|
||||||
|
#[error("could not resolve uprobe target {path}")]
|
||||||
|
InvalidUprobeTarget { path: PathBuf },
|
||||||
|
|
||||||
|
#[error("error resolving symbol: {error}")]
|
||||||
|
UprobeSymbolError { symbol: String, error: String },
|
||||||
|
|
||||||
|
#[error("setsockopt SO_ATTACH_BPF failed: {io_error}")]
|
||||||
|
SocketFilterError { io_error: io::Error },
|
||||||
|
|
||||||
|
#[error("{message}")]
|
||||||
|
Other { message: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ProgramData {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) obj: obj::Program,
|
||||||
|
pub(crate) fd: Option<RawFd>,
|
||||||
|
pub(crate) links: Vec<Rc<RefCell<dyn Link>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Program {
|
||||||
|
KProbe(KProbe),
|
||||||
|
UProbe(UProbe),
|
||||||
|
TracePoint(TracePoint),
|
||||||
|
SocketFilter(SocketFilter),
|
||||||
|
Xdp(Xdp),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
pub fn load(&mut self) -> Result<(), ProgramError> {
|
||||||
|
load_program(self.prog_type(), self.data_mut())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prog_type(&self) -> c_uint {
|
||||||
|
use crate::generated::bpf_prog_type::*;
|
||||||
|
match self {
|
||||||
|
Program::KProbe(_) => BPF_PROG_TYPE_KPROBE,
|
||||||
|
Program::UProbe(_) => BPF_PROG_TYPE_KPROBE,
|
||||||
|
Program::TracePoint(_) => BPF_PROG_TYPE_TRACEPOINT,
|
||||||
|
Program::SocketFilter(_) => BPF_PROG_TYPE_SOCKET_FILTER,
|
||||||
|
Program::Xdp(_) => BPF_PROG_TYPE_XDP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_mut(&mut self) -> &mut ProgramData {
|
||||||
|
match self {
|
||||||
|
Program::KProbe(p) => &mut p.data,
|
||||||
|
Program::UProbe(p) => &mut p.data,
|
||||||
|
Program::TracePoint(p) => &mut p.data,
|
||||||
|
Program::SocketFilter(p) => &mut p.data,
|
||||||
|
Program::Xdp(p) => &mut p.data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgramData {
|
||||||
|
fn fd_or_err(&self) -> Result<RawFd, ProgramError> {
|
||||||
|
self.fd.ok_or(ProgramError::NotLoaded {
|
||||||
|
program: self.name.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ProgramData {
|
||||||
|
fn drop(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_LOG_BUF_SIZE: usize = (std::u32::MAX >> 8) as usize;
|
||||||
|
|
||||||
|
pub struct VerifierLog {
|
||||||
|
buf: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerifierLog {
|
||||||
|
fn new() -> VerifierLog {
|
||||||
|
VerifierLog { buf: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn buf(&mut self) -> &mut Vec<u8> {
|
||||||
|
&mut self.buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grow(&mut self) {
|
||||||
|
self.buf.reserve(cmp::max(
|
||||||
|
1024 * 4,
|
||||||
|
cmp::min(MAX_LOG_BUF_SIZE, self.buf.capacity() * 2),
|
||||||
|
));
|
||||||
|
self.buf.resize(self.buf.capacity(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
if !self.buf.is_empty() {
|
||||||
|
self.buf[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn truncate(&mut self) {
|
||||||
|
if self.buf.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = self
|
||||||
|
.buf
|
||||||
|
.iter()
|
||||||
|
.position(|b| *b == 0)
|
||||||
|
.unwrap_or(self.buf.len() - 1);
|
||||||
|
self.buf.truncate(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_c_str(&self) -> Option<&CStr> {
|
||||||
|
if self.buf.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(CStr::from_bytes_with_nul(&self.buf).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_program(prog_type: c_uint, data: &mut ProgramData) -> Result<(), ProgramError> {
|
||||||
|
let ProgramData { obj, fd, name, .. } = data;
|
||||||
|
if fd.is_some() {
|
||||||
|
return Err(ProgramError::AlreadyLoaded {
|
||||||
|
program: name.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let crate::obj::Program {
|
||||||
|
instructions,
|
||||||
|
license,
|
||||||
|
kernel_version,
|
||||||
|
..
|
||||||
|
} = obj;
|
||||||
|
|
||||||
|
let mut ret = Ok(1);
|
||||||
|
let mut log_buf = VerifierLog::new();
|
||||||
|
for i in 0..3 {
|
||||||
|
log_buf.reset();
|
||||||
|
|
||||||
|
ret = match bpf_load_program(
|
||||||
|
prog_type,
|
||||||
|
instructions,
|
||||||
|
license,
|
||||||
|
(*kernel_version).into(),
|
||||||
|
&mut log_buf,
|
||||||
|
) {
|
||||||
|
Ok(prog_fd) => {
|
||||||
|
*fd = Some(prog_fd as RawFd);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err((_, io_error)) if i == 0 || io_error.raw_os_error() == Some(ENOSPC) => {
|
||||||
|
log_buf.grow();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
x => x,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err((_, io_error)) = ret {
|
||||||
|
log_buf.truncate();
|
||||||
|
return Err(ProgramError::LoadFailed {
|
||||||
|
program: name.clone(),
|
||||||
|
io_error,
|
||||||
|
verifier_log: log_buf.as_c_str().unwrap().to_string_lossy().to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Link: std::fmt::Debug {
|
||||||
|
fn detach(&mut self) -> Result<(), ProgramError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct LinkRef<T: Link> {
|
||||||
|
inner: Weak<RefCell<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Link> LinkRef<T> {
|
||||||
|
fn new(inner: &Rc<RefCell<T>>) -> LinkRef<T> {
|
||||||
|
LinkRef {
|
||||||
|
inner: Rc::downgrade(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Link> Link for LinkRef<T> {
|
||||||
|
fn detach(&mut self) -> Result<(), ProgramError> {
|
||||||
|
if let Some(inner) = self.inner.upgrade() {
|
||||||
|
inner.borrow_mut().detach()
|
||||||
|
} else {
|
||||||
|
Err(ProgramError::AlreadyDetached)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use libc::close;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
syscalls::perf_event_ioctl, RawFd, PERF_EVENT_IOC_DISABLE, PERF_EVENT_IOC_ENABLE,
|
||||||
|
PERF_EVENT_IOC_SET_BPF,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Link, LinkRef, ProgramData, ProgramError};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PerfLink {
|
||||||
|
perf_fd: Option<RawFd>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Link for PerfLink {
|
||||||
|
fn detach(&mut self) -> Result<(), ProgramError> {
|
||||||
|
if let Some(fd) = self.perf_fd.take() {
|
||||||
|
let _ = perf_event_ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||||
|
unsafe { close(fd) };
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ProgramError::AlreadyDetached)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for PerfLink {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = self.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn perf_attach(data: &mut ProgramData, fd: RawFd) -> Result<impl Link, ProgramError> {
|
||||||
|
let link = Rc::new(RefCell::new(PerfLink { perf_fd: Some(fd) }));
|
||||||
|
data.links.push(link.clone());
|
||||||
|
|
||||||
|
let prog_fd = data.fd_or_err()?;
|
||||||
|
perf_event_ioctl(fd, PERF_EVENT_IOC_SET_BPF, prog_fd)
|
||||||
|
.map_err(|(_, io_error)| ProgramError::PerfEventAttachFailed { io_error })?;
|
||||||
|
perf_event_ioctl(fd, PERF_EVENT_IOC_ENABLE, 0)
|
||||||
|
.map_err(|(_, io_error)| ProgramError::PerfEventAttachFailed { io_error })?;
|
||||||
|
|
||||||
|
Ok(LinkRef::new(&link))
|
||||||
|
}
|
@ -0,0 +1,326 @@
|
|||||||
|
use libc::pid_t;
|
||||||
|
use object::{Object, ObjectSymbol};
|
||||||
|
use std::{
|
||||||
|
ffi::CStr,
|
||||||
|
fs,
|
||||||
|
io::{self, BufRead, Cursor, Read},
|
||||||
|
mem,
|
||||||
|
os::raw::c_char,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
generated::bpf_prog_type::BPF_PROG_TYPE_KPROBE,
|
||||||
|
programs::{load_program, ProgramData, ProgramError},
|
||||||
|
syscalls::perf_event_open_probe,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{perf_attach, Link};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref LD_SO_CACHE: Result<LdSoCache, io::Error> = LdSoCache::load("/etc/ld.so.cache");
|
||||||
|
}
|
||||||
|
const LD_SO_CACHE_HEADER: &str = "glibc-ld.so.cache1.1";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct KProbe {
|
||||||
|
pub(crate) data: ProgramData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UProbe {
|
||||||
|
pub(crate) data: ProgramData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KProbe {
|
||||||
|
pub fn load(&mut self) -> Result<(), ProgramError> {
|
||||||
|
load_program(BPF_PROG_TYPE_KPROBE, &mut self.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
self.data.name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attach(
|
||||||
|
&mut self,
|
||||||
|
fn_name: &str,
|
||||||
|
offset: u64,
|
||||||
|
pid: Option<pid_t>,
|
||||||
|
) -> Result<impl Link, ProgramError> {
|
||||||
|
attach(&mut self.data, ProbeKind::KProbe, fn_name, offset, pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UProbe {
|
||||||
|
pub fn load(&mut self) -> Result<(), ProgramError> {
|
||||||
|
load_program(BPF_PROG_TYPE_KPROBE, &mut self.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
self.data.name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attach<T: AsRef<Path>>(
|
||||||
|
&mut self,
|
||||||
|
fn_name: Option<&str>,
|
||||||
|
offset: u64,
|
||||||
|
target: T,
|
||||||
|
pid: Option<pid_t>,
|
||||||
|
) -> Result<impl Link, ProgramError> {
|
||||||
|
let target = target.as_ref();
|
||||||
|
let target_str = &*target.as_os_str().to_string_lossy();
|
||||||
|
|
||||||
|
let mut path = if let Some(pid) = pid {
|
||||||
|
find_lib_in_proc_maps(pid, &target_str).map_err(|io_error| ProgramError::Other {
|
||||||
|
message: format!("error parsing /proc/{}/maps: {}", pid, io_error),
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.is_none() {
|
||||||
|
path = if target.is_absolute() {
|
||||||
|
Some(target_str)
|
||||||
|
} else {
|
||||||
|
let cache =
|
||||||
|
LD_SO_CACHE
|
||||||
|
.as_ref()
|
||||||
|
.map_err(|io_error| ProgramError::InvalidLdSoCache {
|
||||||
|
error_kind: io_error.kind(),
|
||||||
|
})?;
|
||||||
|
cache.resolve(target_str)
|
||||||
|
}
|
||||||
|
.map(String::from)
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = path.ok_or(ProgramError::InvalidUprobeTarget {
|
||||||
|
path: target.to_owned(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let sym_offset = if let Some(fn_name) = fn_name {
|
||||||
|
resolve_symbol(&path, fn_name).map_err(|error| ProgramError::UprobeSymbolError {
|
||||||
|
symbol: fn_name.to_string(),
|
||||||
|
error: error.to_string(),
|
||||||
|
})?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
attach(
|
||||||
|
&mut self.data,
|
||||||
|
ProbeKind::UProbe,
|
||||||
|
&path,
|
||||||
|
sym_offset + offset,
|
||||||
|
pid,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ProbeKind {
|
||||||
|
KProbe,
|
||||||
|
KRetProbe,
|
||||||
|
UProbe,
|
||||||
|
URetProbe,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attach(
|
||||||
|
program_data: &mut ProgramData,
|
||||||
|
kind: ProbeKind,
|
||||||
|
name: &str,
|
||||||
|
offset: u64,
|
||||||
|
pid: Option<pid_t>,
|
||||||
|
) -> Result<impl Link, ProgramError> {
|
||||||
|
use ProbeKind::*;
|
||||||
|
|
||||||
|
let perf_ty = read_sys_fs_perf_type(match kind {
|
||||||
|
KProbe | KRetProbe => "kprobe",
|
||||||
|
UProbe | URetProbe => "uprobe",
|
||||||
|
})?;
|
||||||
|
let ret_bit = match kind {
|
||||||
|
KRetProbe => Some(read_sys_fs_perf_ret_probe("kprobe")?),
|
||||||
|
URetProbe => Some(read_sys_fs_perf_ret_probe("uprobe")?),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let fd = perf_event_open_probe(perf_ty, ret_bit, name, offset, pid)
|
||||||
|
.map_err(|(_code, io_error)| ProgramError::PerfEventOpenFailed { io_error })?
|
||||||
|
as i32;
|
||||||
|
|
||||||
|
perf_attach(program_data, fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn proc_maps_libs(pid: pid_t) -> Result<Vec<(String, String)>, io::Error> {
|
||||||
|
let maps_file = format!("/proc/{}/maps", pid);
|
||||||
|
let data = fs::read_to_string(maps_file)?;
|
||||||
|
|
||||||
|
Ok(data
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
|
let line = line.split_whitespace().last()?;
|
||||||
|
if line.starts_with('/') {
|
||||||
|
let path = PathBuf::from(line);
|
||||||
|
let key = path.file_name().unwrap().to_string_lossy().into_owned();
|
||||||
|
Some((key, path.to_string_lossy().to_string()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_lib_in_proc_maps(pid: pid_t, lib: &str) -> Result<Option<String>, io::Error> {
|
||||||
|
let libs = proc_maps_libs(pid)?;
|
||||||
|
|
||||||
|
let ret = if lib.contains(".so") {
|
||||||
|
libs.iter().find(|(k, _)| k.as_str().starts_with(lib))
|
||||||
|
} else {
|
||||||
|
let lib = lib.to_string();
|
||||||
|
let lib1 = lib.clone() + ".so";
|
||||||
|
let lib2 = lib + "-";
|
||||||
|
libs.iter()
|
||||||
|
.find(|(k, _)| k.starts_with(&lib1) || k.starts_with(&lib2))
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ret.map(|(_, v)| v.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct CacheEntry {
|
||||||
|
key: String,
|
||||||
|
value: String,
|
||||||
|
flags: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct LdSoCache {
|
||||||
|
entries: Vec<CacheEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LdSoCache {
|
||||||
|
pub fn load<T: AsRef<Path>>(path: T) -> Result<Self, io::Error> {
|
||||||
|
let data = fs::read(path)?;
|
||||||
|
Self::parse(&data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(data: &[u8]) -> Result<Self, io::Error> {
|
||||||
|
let mut cursor = Cursor::new(data);
|
||||||
|
|
||||||
|
let read_u32 = |cursor: &mut Cursor<_>| -> Result<u32, io::Error> {
|
||||||
|
let mut buf = [0u8; mem::size_of::<u32>()];
|
||||||
|
cursor.read_exact(&mut buf)?;
|
||||||
|
|
||||||
|
Ok(u32::from_ne_bytes(buf))
|
||||||
|
};
|
||||||
|
|
||||||
|
let read_i32 = |cursor: &mut Cursor<_>| -> Result<i32, io::Error> {
|
||||||
|
let mut buf = [0u8; mem::size_of::<i32>()];
|
||||||
|
cursor.read_exact(&mut buf)?;
|
||||||
|
|
||||||
|
Ok(i32::from_ne_bytes(buf))
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = [0u8; LD_SO_CACHE_HEADER.len()];
|
||||||
|
cursor.read_exact(&mut buf)?;
|
||||||
|
let header = std::str::from_utf8(&buf).or(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"invalid ld.so.cache header",
|
||||||
|
)))?;
|
||||||
|
if header != LD_SO_CACHE_HEADER {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"invalid ld.so.cache header",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let num_entries = read_u32(&mut cursor)?;
|
||||||
|
let _str_tab_len = read_u32(&mut cursor)?;
|
||||||
|
cursor.consume(5 * mem::size_of::<u32>());
|
||||||
|
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
for _ in 0..num_entries {
|
||||||
|
let flags = read_i32(&mut cursor)?;
|
||||||
|
let k_pos = read_u32(&mut cursor)? as usize;
|
||||||
|
let v_pos = read_u32(&mut cursor)? as usize;
|
||||||
|
cursor.consume(12);
|
||||||
|
let key =
|
||||||
|
unsafe { CStr::from_ptr(cursor.get_ref()[k_pos..].as_ptr() as *const c_char) }
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
let value =
|
||||||
|
unsafe { CStr::from_ptr(cursor.get_ref()[v_pos..].as_ptr() as *const c_char) }
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
entries.push(CacheEntry { key, value, flags });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LdSoCache { entries })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(&self, lib: &str) -> Option<&str> {
|
||||||
|
let lib = if !lib.contains(".so") {
|
||||||
|
lib.to_string() + ".so"
|
||||||
|
} else {
|
||||||
|
lib.to_string()
|
||||||
|
};
|
||||||
|
self.entries
|
||||||
|
.iter()
|
||||||
|
.find(|entry| entry.key.starts_with(&lib))
|
||||||
|
.map(|entry| entry.value.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
enum ResolveSymbolError {
|
||||||
|
#[error("io error {0}")]
|
||||||
|
Io(#[from] io::Error),
|
||||||
|
|
||||||
|
#[error("error parsing ELF {0}")]
|
||||||
|
Object(#[from] object::Error),
|
||||||
|
|
||||||
|
#[error("unknown symbol {0}")]
|
||||||
|
Unknown(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_symbol(path: &str, symbol: &str) -> Result<u64, ResolveSymbolError> {
|
||||||
|
let data = fs::read(path)?;
|
||||||
|
let obj = object::read::File::parse(&data)?;
|
||||||
|
|
||||||
|
obj.dynamic_symbols()
|
||||||
|
.chain(obj.symbols())
|
||||||
|
.find(|sym| sym.name().map(|name| name == symbol).unwrap_or(false))
|
||||||
|
.map(|s| s.address())
|
||||||
|
.ok_or_else(|| ResolveSymbolError::Unknown(symbol.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_sys_fs_perf_type(pmu: &str) -> Result<u32, ProgramError> {
|
||||||
|
let file = format!("/sys/bus/event_source/devices/{}/type", pmu);
|
||||||
|
|
||||||
|
let perf_ty = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
|
||||||
|
message: format!("error parsing {}: {}", file, e),
|
||||||
|
})?;
|
||||||
|
let perf_ty = perf_ty
|
||||||
|
.trim()
|
||||||
|
.parse::<u32>()
|
||||||
|
.map_err(|e| ProgramError::Other {
|
||||||
|
message: format!("error parsing {}: {}", file, e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(perf_ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_sys_fs_perf_ret_probe(pmu: &str) -> Result<u32, ProgramError> {
|
||||||
|
let file = format!("/sys/bus/event_source/devices/{}/format/retprobe", pmu);
|
||||||
|
|
||||||
|
let data = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
|
||||||
|
message: format!("error parsing {}: {}", file, e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut parts = data.trim().splitn(2, ":").skip(1);
|
||||||
|
let config = parts.next().ok_or(ProgramError::Other {
|
||||||
|
message: format!("error parsing {}: `{}'", file, data),
|
||||||
|
})?;
|
||||||
|
config.parse::<u32>().map_err(|e| ProgramError::Other {
|
||||||
|
message: format!("error parsing {}: {}", file, e),
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
use std::{io, mem, os::unix::prelude::RawFd};
|
||||||
|
|
||||||
|
use libc::{setsockopt, SOL_SOCKET, SO_ATTACH_BPF};
|
||||||
|
|
||||||
|
use crate::generated::bpf_prog_type::BPF_PROG_TYPE_SOCKET_FILTER;
|
||||||
|
|
||||||
|
use super::{load_program, ProgramData, ProgramError};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SocketFilter {
|
||||||
|
pub(crate) data: ProgramData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SocketFilter {
|
||||||
|
pub fn load(&mut self) -> Result<(), ProgramError> {
|
||||||
|
load_program(BPF_PROG_TYPE_SOCKET_FILTER, &mut self.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attach(&self, socket: RawFd) -> Result<(), ProgramError> {
|
||||||
|
let prog_fd = self.data.fd_or_err()?;
|
||||||
|
|
||||||
|
let ret = unsafe {
|
||||||
|
setsockopt(
|
||||||
|
socket,
|
||||||
|
SOL_SOCKET,
|
||||||
|
SO_ATTACH_BPF,
|
||||||
|
&prog_fd as *const _ as *const _,
|
||||||
|
mem::size_of::<RawFd>() as u32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ret < 0 {
|
||||||
|
return Err(ProgramError::SocketFilterError {
|
||||||
|
io_error: io::Error::last_os_error(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
generated::bpf_prog_type::BPF_PROG_TYPE_TRACEPOINT, syscalls::perf_event_open_trace_point,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{load_program, perf_attach, Link, ProgramData, ProgramError};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TracePoint {
|
||||||
|
pub(crate) data: ProgramData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TracePoint {
|
||||||
|
pub fn load(&mut self) -> Result<(), ProgramError> {
|
||||||
|
load_program(BPF_PROG_TYPE_TRACEPOINT, &mut self.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attach(&mut self, category: &str, name: &str) -> Result<impl Link, ProgramError> {
|
||||||
|
let id = read_sys_fs_trace_point_id(category, name)?;
|
||||||
|
let fd = perf_event_open_trace_point(id)
|
||||||
|
.map_err(|(_code, io_error)| ProgramError::PerfEventOpenFailed { io_error })?
|
||||||
|
as i32;
|
||||||
|
|
||||||
|
perf_attach(&mut self.data, fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_sys_fs_trace_point_id(category: &str, name: &str) -> Result<u32, ProgramError> {
|
||||||
|
let file = format!("/sys/kernel/debug/tracing/events/{}/{}/id", category, name);
|
||||||
|
|
||||||
|
let id = fs::read_to_string(&file).map_err(|e| ProgramError::Other {
|
||||||
|
message: format!("error parsing {}: {}", file, e),
|
||||||
|
})?;
|
||||||
|
let id = id.trim().parse::<u32>().map_err(|e| ProgramError::Other {
|
||||||
|
message: format!("error parsing {}: {}", file, e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(id)
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
use libc::if_nametoindex;
|
||||||
|
|
||||||
|
use crate::generated::{bpf_attach_type::BPF_XDP, bpf_prog_type::BPF_PROG_TYPE_XDP};
|
||||||
|
use crate::syscalls::bpf_link_create;
|
||||||
|
use crate::RawFd;
|
||||||
|
|
||||||
|
use super::{load_program, ProgramData, ProgramError};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Xdp {
|
||||||
|
pub(crate) data: ProgramData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Xdp {
|
||||||
|
pub fn load(&mut self) -> Result<(), ProgramError> {
|
||||||
|
load_program(BPF_PROG_TYPE_XDP, &mut self.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
self.data.name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attach(&self, interface: &str) -> Result<(), ProgramError> {
|
||||||
|
let prog_fd = self.data.fd_or_err()?;
|
||||||
|
|
||||||
|
let c_interface = CString::new(interface).unwrap();
|
||||||
|
let if_index = unsafe { if_nametoindex(c_interface.as_ptr()) } as RawFd;
|
||||||
|
if if_index == 0 {
|
||||||
|
return Err(ProgramError::UnkownInterface {
|
||||||
|
name: interface.to_string(),
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let link_fd = bpf_link_create(prog_fd, if_index, BPF_XDP, 0).map_err(|(_, io_error)| {
|
||||||
|
ProgramError::BpfLinkCreateFailed {
|
||||||
|
program: self.name(),
|
||||||
|
io_error,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,180 @@
|
|||||||
|
use std::{
|
||||||
|
cmp,
|
||||||
|
ffi::CStr,
|
||||||
|
io,
|
||||||
|
mem::{self, MaybeUninit},
|
||||||
|
slice,
|
||||||
|
};
|
||||||
|
|
||||||
|
use libc::{c_long, c_uint, ENOENT};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
bpf_map_def,
|
||||||
|
generated::{bpf_attach_type, bpf_attr, bpf_cmd, bpf_insn},
|
||||||
|
programs::VerifierLog,
|
||||||
|
syscalls::SysResult,
|
||||||
|
RawFd, BPF_OBJ_NAME_LEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{syscall, Syscall};
|
||||||
|
|
||||||
|
pub(crate) fn bpf_create_map(name: &CStr, def: &bpf_map_def) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_1 };
|
||||||
|
u.map_type = def.map_type;
|
||||||
|
u.key_size = def.key_size;
|
||||||
|
u.value_size = def.value_size;
|
||||||
|
u.max_entries = def.max_entries;
|
||||||
|
u.map_flags = def.map_flags;
|
||||||
|
|
||||||
|
// u.map_name is 16 bytes max and must be NULL terminated
|
||||||
|
let name_len = cmp::min(name.to_bytes().len(), BPF_OBJ_NAME_LEN - 1);
|
||||||
|
u.map_name[..name_len]
|
||||||
|
.copy_from_slice(unsafe { slice::from_raw_parts(name.as_ptr(), name_len) });
|
||||||
|
|
||||||
|
sys_bpf(bpf_cmd::BPF_MAP_CREATE, &attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bpf_load_program(
|
||||||
|
ty: c_uint,
|
||||||
|
insns: &[bpf_insn],
|
||||||
|
license: &CStr,
|
||||||
|
kernel_version: u32,
|
||||||
|
log: &mut VerifierLog,
|
||||||
|
) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_3 };
|
||||||
|
u.prog_type = ty;
|
||||||
|
u.expected_attach_type = 0;
|
||||||
|
u.insns = insns.as_ptr() as u64;
|
||||||
|
u.insn_cnt = insns.len() as u32;
|
||||||
|
u.license = license.as_ptr() as u64;
|
||||||
|
u.kern_version = kernel_version;
|
||||||
|
let log_buf = log.buf();
|
||||||
|
if log_buf.capacity() > 0 {
|
||||||
|
u.log_level = 7;
|
||||||
|
u.log_buf = log_buf.as_mut_ptr() as u64;
|
||||||
|
u.log_size = log_buf.capacity() as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_bpf(bpf_cmd::BPF_PROG_LOAD, &attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup<K, V: crate::Pod>(
|
||||||
|
fd: RawFd,
|
||||||
|
key: &K,
|
||||||
|
flags: u64,
|
||||||
|
cmd: bpf_cmd::Type,
|
||||||
|
) -> Result<Option<V>, (c_long, io::Error)> {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
let mut value = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_2 };
|
||||||
|
u.map_fd = fd as u32;
|
||||||
|
u.key = key as *const _ as u64;
|
||||||
|
u.__bindgen_anon_1.value = &mut value as *mut _ as u64;
|
||||||
|
u.flags = flags;
|
||||||
|
|
||||||
|
match sys_bpf(cmd, &attr) {
|
||||||
|
Ok(_) => Ok(Some(unsafe { value.assume_init() })),
|
||||||
|
Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bpf_map_lookup_elem<K, V: crate::Pod>(
|
||||||
|
fd: RawFd,
|
||||||
|
key: &K,
|
||||||
|
flags: u64,
|
||||||
|
) -> Result<Option<V>, (c_long, io::Error)> {
|
||||||
|
lookup(fd, key, flags, bpf_cmd::BPF_MAP_LOOKUP_ELEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bpf_map_lookup_and_delete_elem<K, V: crate::Pod>(
|
||||||
|
fd: RawFd,
|
||||||
|
key: &K,
|
||||||
|
) -> Result<Option<V>, (c_long, io::Error)> {
|
||||||
|
lookup(fd, key, 0, bpf_cmd::BPF_MAP_LOOKUP_AND_DELETE_ELEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bpf_map_update_elem<K, V>(fd: RawFd, key: &K, value: &V, flags: u64) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_2 };
|
||||||
|
u.map_fd = fd as u32;
|
||||||
|
u.key = key as *const _ as u64;
|
||||||
|
u.__bindgen_anon_1.value = value as *const _ as u64;
|
||||||
|
u.flags = flags;
|
||||||
|
|
||||||
|
sys_bpf(bpf_cmd::BPF_MAP_UPDATE_ELEM, &attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bpf_map_update_elem_ptr<K, V>(
|
||||||
|
fd: RawFd,
|
||||||
|
key: *const K,
|
||||||
|
value: *const V,
|
||||||
|
flags: u64,
|
||||||
|
) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_2 };
|
||||||
|
u.map_fd = fd as u32;
|
||||||
|
u.key = key as u64;
|
||||||
|
u.__bindgen_anon_1.value = value as u64;
|
||||||
|
u.flags = flags;
|
||||||
|
|
||||||
|
sys_bpf(bpf_cmd::BPF_MAP_UPDATE_ELEM, &attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bpf_map_delete_elem<K>(fd: RawFd, key: &K) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_2 };
|
||||||
|
u.map_fd = fd as u32;
|
||||||
|
u.key = key as *const _ as u64;
|
||||||
|
|
||||||
|
sys_bpf(bpf_cmd::BPF_MAP_DELETE_ELEM, &attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bpf_map_get_next_key<K>(
|
||||||
|
fd: RawFd,
|
||||||
|
key: Option<&K>,
|
||||||
|
) -> Result<Option<K>, (c_long, io::Error)> {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
let mut next_key = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
let u = unsafe { &mut attr.__bindgen_anon_2 };
|
||||||
|
u.map_fd = fd as u32;
|
||||||
|
if let Some(key) = key {
|
||||||
|
u.key = key as *const _ as u64;
|
||||||
|
}
|
||||||
|
u.__bindgen_anon_1.next_key = &mut next_key as *mut _ as u64;
|
||||||
|
|
||||||
|
match sys_bpf(bpf_cmd::BPF_MAP_GET_NEXT_KEY, &attr) {
|
||||||
|
Ok(_) => Ok(Some(unsafe { next_key.assume_init() })),
|
||||||
|
Err((_, io_error)) if io_error.raw_os_error() == Some(ENOENT) => Ok(None),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bpf_link_create(
|
||||||
|
prog_fd: RawFd,
|
||||||
|
target_fd: RawFd,
|
||||||
|
attach_type: bpf_attach_type::Type,
|
||||||
|
flags: u32,
|
||||||
|
) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<bpf_attr>() };
|
||||||
|
|
||||||
|
attr.link_create.prog_fd = prog_fd as u32;
|
||||||
|
attr.link_create.__bindgen_anon_1.target_fd = target_fd as u32;
|
||||||
|
attr.link_create.attach_type = attach_type;
|
||||||
|
attr.link_create.flags = flags;
|
||||||
|
|
||||||
|
sys_bpf(bpf_cmd::BPF_LINK_CREATE, &attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sys_bpf<'a>(cmd: bpf_cmd::Type, attr: &'a bpf_attr) -> SysResult {
|
||||||
|
syscall(Syscall::Bpf { cmd, attr })
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
use std::{cell::RefCell, io, ptr};
|
||||||
|
|
||||||
|
use libc::c_void;
|
||||||
|
|
||||||
|
use super::{SysResult, Syscall};
|
||||||
|
|
||||||
|
type SyscallFn = unsafe fn(Syscall) -> SysResult;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
thread_local! {
|
||||||
|
pub(crate) static TEST_SYSCALL: RefCell<SyscallFn> = RefCell::new(test_syscall);
|
||||||
|
pub(crate) static TEST_MMAP_RET: RefCell<*mut c_void> = RefCell::new(ptr::null_mut());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
unsafe fn test_syscall(_call: Syscall) -> SysResult {
|
||||||
|
return Err((-1, io::Error::from_raw_os_error(libc::EINVAL)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn override_syscall(call: unsafe fn(Syscall) -> SysResult) {
|
||||||
|
TEST_SYSCALL.with(|test_impl| *test_impl.borrow_mut() = call);
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
mod bpf;
|
||||||
|
mod perf_event;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod fake;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use libc::{c_int, c_long, c_ulong, pid_t};
|
||||||
|
|
||||||
|
pub(crate) use bpf::*;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) use fake::*;
|
||||||
|
pub(crate) use perf_event::*;
|
||||||
|
|
||||||
|
use crate::generated::{bpf_attr, bpf_cmd, perf_event_attr};
|
||||||
|
|
||||||
|
pub(crate) type SysResult = Result<c_long, (c_long, io::Error)>;
|
||||||
|
|
||||||
|
#[cfg_attr(test, allow(dead_code))]
|
||||||
|
pub(crate) enum Syscall<'a> {
|
||||||
|
Bpf {
|
||||||
|
cmd: bpf_cmd::Type,
|
||||||
|
attr: &'a bpf_attr,
|
||||||
|
},
|
||||||
|
PerfEventOpen {
|
||||||
|
attr: perf_event_attr,
|
||||||
|
pid: pid_t,
|
||||||
|
cpu: i32,
|
||||||
|
group: i32,
|
||||||
|
flags: u32,
|
||||||
|
},
|
||||||
|
PerfEventIoctl {
|
||||||
|
fd: c_int,
|
||||||
|
request: c_ulong,
|
||||||
|
arg: c_int,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn syscall(call: Syscall) -> SysResult {
|
||||||
|
#[cfg(not(test))]
|
||||||
|
return unsafe { syscall_impl(call) };
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
return TEST_SYSCALL.with(|test_impl| unsafe { test_impl.borrow()(call) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
unsafe fn syscall_impl(call: Syscall) -> SysResult {
|
||||||
|
use libc::{SYS_bpf, SYS_perf_event_open};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use Syscall::*;
|
||||||
|
let ret = match call {
|
||||||
|
Bpf { cmd, attr } => libc::syscall(SYS_bpf, cmd, attr, mem::size_of::<bpf_attr>()),
|
||||||
|
PerfEventOpen {
|
||||||
|
attr,
|
||||||
|
pid,
|
||||||
|
cpu,
|
||||||
|
group,
|
||||||
|
flags,
|
||||||
|
} => libc::syscall(SYS_perf_event_open, &attr, pid, cpu, group, flags),
|
||||||
|
PerfEventIoctl { fd, request, arg } => libc::ioctl(fd, request, arg) as i64,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ret < 0 {
|
||||||
|
return Err((ret, io::Error::last_os_error()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
use std::{ffi::CString, mem};
|
||||||
|
|
||||||
|
use libc::{c_int, c_ulong, pid_t};
|
||||||
|
|
||||||
|
use crate::generated::{
|
||||||
|
perf_event_attr,
|
||||||
|
perf_event_sample_format::PERF_SAMPLE_RAW,
|
||||||
|
perf_sw_ids::PERF_COUNT_SW_BPF_OUTPUT,
|
||||||
|
perf_type_id::{PERF_TYPE_SOFTWARE, PERF_TYPE_TRACEPOINT},
|
||||||
|
PERF_FLAG_FD_CLOEXEC,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{syscall, SysResult, Syscall};
|
||||||
|
|
||||||
|
pub(crate) fn perf_event_open(cpu: c_int) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<perf_event_attr>() };
|
||||||
|
|
||||||
|
attr.config = PERF_COUNT_SW_BPF_OUTPUT as u64;
|
||||||
|
attr.size = mem::size_of::<perf_event_attr>() as u32;
|
||||||
|
attr.type_ = PERF_TYPE_SOFTWARE;
|
||||||
|
attr.sample_type = PERF_SAMPLE_RAW as u64;
|
||||||
|
attr.__bindgen_anon_1.sample_period = 1;
|
||||||
|
attr.__bindgen_anon_2.wakeup_events = 1;
|
||||||
|
|
||||||
|
syscall(Syscall::PerfEventOpen {
|
||||||
|
attr,
|
||||||
|
pid: -1,
|
||||||
|
cpu,
|
||||||
|
group: -1,
|
||||||
|
flags: PERF_FLAG_FD_CLOEXEC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn perf_event_open_probe(
|
||||||
|
ty: u32,
|
||||||
|
ret_bit: Option<u32>,
|
||||||
|
name: &str,
|
||||||
|
offset: u64,
|
||||||
|
pid: Option<pid_t>,
|
||||||
|
) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<perf_event_attr>() };
|
||||||
|
|
||||||
|
if let Some(ret_bit) = ret_bit {
|
||||||
|
attr.config = 1 << ret_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
let c_name = CString::new(name).unwrap();
|
||||||
|
|
||||||
|
attr.size = mem::size_of::<perf_event_attr>() as u32;
|
||||||
|
attr.type_ = ty;
|
||||||
|
attr.__bindgen_anon_3.config1 = c_name.as_ptr() as u64;
|
||||||
|
attr.__bindgen_anon_4.config2 = offset;
|
||||||
|
|
||||||
|
let cpu = if pid.is_some() { -1 } else { 0 };
|
||||||
|
let pid = pid.unwrap_or(-1);
|
||||||
|
|
||||||
|
syscall(Syscall::PerfEventOpen {
|
||||||
|
attr,
|
||||||
|
pid,
|
||||||
|
cpu,
|
||||||
|
group: -1,
|
||||||
|
flags: PERF_FLAG_FD_CLOEXEC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn perf_event_open_trace_point(id: u32) -> SysResult {
|
||||||
|
let mut attr = unsafe { mem::zeroed::<perf_event_attr>() };
|
||||||
|
|
||||||
|
attr.size = mem::size_of::<perf_event_attr>() as u32;
|
||||||
|
attr.type_ = PERF_TYPE_TRACEPOINT;
|
||||||
|
attr.config = id as u64;
|
||||||
|
|
||||||
|
syscall(Syscall::PerfEventOpen {
|
||||||
|
attr,
|
||||||
|
pid: -1,
|
||||||
|
cpu: 0,
|
||||||
|
group: -1,
|
||||||
|
flags: PERF_FLAG_FD_CLOEXEC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn perf_event_ioctl(fd: c_int, request: c_ulong, arg: c_int) -> SysResult {
|
||||||
|
let call = Syscall::PerfEventIoctl { fd, request, arg };
|
||||||
|
#[cfg(not(test))]
|
||||||
|
return syscall(call);
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
return crate::syscalls::TEST_SYSCALL.with(|test_impl| unsafe { test_impl.borrow()(call) });
|
||||||
|
}
|
Loading…
Reference in New Issue