use std::{
borrow::{Borrow, BorrowMut},
os::fd::AsFd as _,
};
use aya_obj::generated::BPF_F_STACK_BUILD_ID;
use crate::{
maps::{IterableMap, MapData, MapError, MapIter, MapKeys, hash_map},
sys::{SyscallError, bpf_map_lookup_elem_ptr},
};
#[derive(Debug)]
#[doc(alias = "BPF_MAP_TYPE_STACK_TRACE")]
pub struct StackTraceMap<T> {
pub(crate) inner: T,
max_stack_depth: usize,
}
type StackEntry = u64;
impl<T: Borrow<MapData>> StackTraceMap<T> {
pub(crate) fn new(map: T) -> Result<Self, MapError> {
let data = map.borrow();
let key_size = data.obj.key_size() as usize;
let expected_key = size_of::<u32>();
if key_size != expected_key {
return Err(MapError::InvalidKeySize {
size: key_size,
expected: expected_key,
});
}
let flags = data.obj.map_flags();
if flags & BPF_F_STACK_BUILD_ID != 0 {
return Err(MapError::UnsupportedMapFlags {
flags,
reason: "StackTraceMap does not support bpf_stack_build_id entries",
});
}
let value_size = data.obj.value_size() as usize;
let expected_stride = size_of::<StackEntry>();
if value_size == 0 || !value_size.is_multiple_of(expected_stride) {
return Err(MapError::InvalidValueStride {
size: value_size,
stride: expected_stride,
});
}
let max_stack_depth = value_size / expected_stride;
Ok(Self {
inner: map,
max_stack_depth,
})
}
pub fn get(&self, stack_id: &u32, flags: u64) -> Result<StackTrace, MapError> {
let fd = self.inner.borrow().fd().as_fd();
let mut frames: Vec<StackEntry> = vec![0; self.max_stack_depth];
bpf_map_lookup_elem_ptr(fd, Some(stack_id), frames.as_mut_ptr(), flags)
.map_err(|io_error| SyscallError {
call: "bpf_map_lookup_elem",
io_error,
})?
.ok_or(MapError::KeyNotFound)?;
let frames = frames
.into_iter()
.take_while(|ip| *ip != 0)
.map(|ip| StackFrame { ip })
.collect::<Vec<_>>();
Ok(StackTrace {
id: *stack_id,
frames,
})
}
pub fn iter(&self) -> MapIter<'_, u32, StackTrace, Self> {
MapIter::new(self)
}
pub fn stack_ids(&self) -> MapKeys<'_, u32> {
MapKeys::new(self.inner.borrow())
}
}
impl<T: Borrow<MapData>> IterableMap<u32, StackTrace> for StackTraceMap<T> {
fn map(&self) -> &MapData {
self.inner.borrow()
}
fn get(&self, key: &u32) -> Result<StackTrace, MapError> {
self.get(key, 0)
}
}
impl<'a, T: Borrow<MapData>> IntoIterator for &'a StackTraceMap<T> {
type Item = Result<(u32, StackTrace), MapError>;
type IntoIter = MapIter<'a, u32, StackTrace, StackTraceMap<T>>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T: BorrowMut<MapData>> StackTraceMap<T> {
pub fn remove(&mut self, stack_id: &u32) -> Result<(), MapError> {
hash_map::remove(self.inner.borrow_mut(), stack_id)
}
}
pub struct StackTrace {
pub id: u32,
frames: Vec<StackFrame>,
}
impl StackTrace {
pub fn frames(&self) -> &[StackFrame] {
&self.frames
}
}
pub struct StackFrame {
pub ip: u64,
}