Skip to main content

libbpf_rs/
perf_buffer.rs

1use core::ffi::c_void;
2use std::fmt::Debug;
3use std::fmt::Formatter;
4use std::fmt::Result as FmtResult;
5use std::os::unix::prelude::AsRawFd;
6use std::ptr;
7use std::ptr::NonNull;
8use std::slice;
9use std::time::Duration;
10
11use crate::util;
12use crate::util::validate_bpf_ret;
13use crate::AsRawLibbpf;
14use crate::Error;
15use crate::ErrorExt as _;
16use crate::MapCore;
17use crate::MapType;
18use crate::Result;
19
20type SampleCb<'b> = Box<dyn FnMut(i32, &[u8]) + 'b>;
21type LostCb<'b> = Box<dyn FnMut(i32, u64) + 'b>;
22
23struct CbStruct<'b> {
24    sample_cb: Option<SampleCb<'b>>,
25    lost_cb: Option<LostCb<'b>>,
26}
27
28impl Debug for CbStruct<'_> {
29    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
30        let Self { sample_cb, lost_cb } = self;
31        f.debug_struct("CbStruct")
32            .field("sample_cb", &sample_cb.as_ref().map(|cb| &cb as *const _))
33            .field("lost_cb", &lost_cb.as_ref().map(|cb| &cb as *const _))
34            .finish()
35    }
36}
37
38/// Builds [`PerfBuffer`] instances.
39pub struct PerfBufferBuilder<'a, 'b, M>
40where
41    M: MapCore,
42{
43    map: &'a M,
44    pages: usize,
45    sample_cb: Option<SampleCb<'b>>,
46    lost_cb: Option<LostCb<'b>>,
47}
48
49impl<'a, M> PerfBufferBuilder<'a, '_, M>
50where
51    M: MapCore,
52{
53    /// Create a new `PerfBufferBuilder` using the provided `MapCore`
54    /// object.
55    pub fn new(map: &'a M) -> Self {
56        Self {
57            map,
58            pages: 64,
59            sample_cb: None,
60            lost_cb: None,
61        }
62    }
63}
64
65impl<'a, 'b, M> PerfBufferBuilder<'a, 'b, M>
66where
67    M: MapCore,
68{
69    /// Callback to run when a sample is received.
70    ///
71    /// This callback provides a raw byte slice. You may find libraries such as
72    /// [`plain`](https://crates.io/crates/plain) helpful.
73    ///
74    /// Callback arguments are: `(cpu, data)`.
75    pub fn sample_cb<F>(self, cb: F) -> Self
76    where
77        F: FnMut(i32, &[u8]) + 'b,
78    {
79        PerfBufferBuilder {
80            map: self.map,
81            pages: self.pages,
82            sample_cb: Some(Box::new(cb)),
83            lost_cb: self.lost_cb,
84        }
85    }
86
87    /// Callback to run when a sample is received.
88    ///
89    /// Callback arguments are: `(cpu, lost_count)`.
90    pub fn lost_cb<F>(self, cb: F) -> Self
91    where
92        F: FnMut(i32, u64) + 'b,
93    {
94        PerfBufferBuilder {
95            map: self.map,
96            pages: self.pages,
97            sample_cb: self.sample_cb,
98            lost_cb: Some(Box::new(cb)),
99        }
100    }
101
102    /// The number of pages to size the ring buffer.
103    pub fn pages(self, pages: usize) -> Self {
104        PerfBufferBuilder {
105            map: self.map,
106            pages,
107            sample_cb: self.sample_cb,
108            lost_cb: self.lost_cb,
109        }
110    }
111
112    /// Build the `PerfBuffer` object as configured.
113    #[doc(alias = "perf_buffer__new")]
114    pub fn build(self) -> Result<PerfBuffer<'b>> {
115        if self.map.map_type() != MapType::PerfEventArray {
116            return Err(Error::with_invalid_data("Must use a PerfEventArray map"));
117        }
118
119        if !self.pages.is_power_of_two() {
120            return Err(Error::with_invalid_data("Page count must be power of two"));
121        }
122
123        let c_sample_cb: libbpf_sys::perf_buffer_sample_fn = if self.sample_cb.is_some() {
124            Some(Self::call_sample_cb)
125        } else {
126            None
127        };
128
129        let c_lost_cb: libbpf_sys::perf_buffer_lost_fn = if self.lost_cb.is_some() {
130            Some(Self::call_lost_cb)
131        } else {
132            None
133        };
134
135        let callback_struct_ptr = Box::into_raw(Box::new(CbStruct {
136            sample_cb: self.sample_cb,
137            lost_cb: self.lost_cb,
138        }));
139
140        let ptr = unsafe {
141            libbpf_sys::perf_buffer__new(
142                self.map.as_fd().as_raw_fd(),
143                self.pages as libbpf_sys::size_t,
144                c_sample_cb,
145                c_lost_cb,
146                callback_struct_ptr.cast(),
147                ptr::null(),
148            )
149        };
150        let ptr = validate_bpf_ret(ptr).context("failed to create perf buffer")?;
151        let pb = PerfBuffer {
152            ptr,
153            _cb_struct: unsafe { Box::from_raw(callback_struct_ptr) },
154        };
155        Ok(pb)
156    }
157
158    unsafe extern "C" fn call_sample_cb(ctx: *mut c_void, cpu: i32, data: *mut c_void, size: u32) {
159        let callback_struct = ctx.cast::<CbStruct<'_>>();
160
161        if let Some(cb) = unsafe { &mut (*callback_struct).sample_cb } {
162            let slice = unsafe { slice::from_raw_parts(data as *const u8, size as usize) };
163            cb(cpu, slice);
164        }
165    }
166
167    unsafe extern "C" fn call_lost_cb(ctx: *mut c_void, cpu: i32, count: u64) {
168        let callback_struct = ctx.cast::<CbStruct<'_>>();
169
170        if let Some(cb) = unsafe { &mut (*callback_struct).lost_cb } {
171            cb(cpu, count);
172        }
173    }
174}
175
176impl<M> Debug for PerfBufferBuilder<'_, '_, M>
177where
178    M: MapCore,
179{
180    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
181        let Self {
182            map,
183            pages,
184            sample_cb,
185            lost_cb,
186        } = self;
187        f.debug_struct("PerfBufferBuilder")
188            .field("map", map)
189            .field("pages", pages)
190            .field("sample_cb", &sample_cb.as_ref().map(|cb| &cb as *const _))
191            .field("lost_cb", &lost_cb.as_ref().map(|cb| &cb as *const _))
192            .finish()
193    }
194}
195
196/// Represents a special kind of [`MapCore`]. Typically used to transfer data between
197/// [`Program`][crate::Program]s and userspace.
198#[derive(Debug)]
199#[doc(alias = "perf_buffer")]
200pub struct PerfBuffer<'b> {
201    ptr: NonNull<libbpf_sys::perf_buffer>,
202    // Hold onto the box so it'll get dropped when PerfBuffer is dropped
203    _cb_struct: Box<CbStruct<'b>>,
204}
205
206// TODO: Document methods.
207#[expect(missing_docs)]
208impl PerfBuffer<'_> {
209    #[doc(alias = "perf_buffer__epoll_fd")]
210    pub fn epoll_fd(&self) -> i32 {
211        unsafe { libbpf_sys::perf_buffer__epoll_fd(self.ptr.as_ptr()) }
212    }
213
214    #[doc(alias = "perf_buffer__poll")]
215    pub fn poll(&self, timeout: Duration) -> Result<()> {
216        let ret =
217            unsafe { libbpf_sys::perf_buffer__poll(self.ptr.as_ptr(), timeout.as_millis() as i32) };
218        util::parse_ret(ret)
219    }
220
221    #[doc(alias = "perf_buffer__consume")]
222    pub fn consume(&self) -> Result<()> {
223        let ret = unsafe { libbpf_sys::perf_buffer__consume(self.ptr.as_ptr()) };
224        util::parse_ret(ret)
225    }
226
227    #[doc(alias = "perf_buffer__consume_buffer")]
228    pub fn consume_buffer(&self, buf_idx: usize) -> Result<()> {
229        let ret = unsafe {
230            libbpf_sys::perf_buffer__consume_buffer(
231                self.ptr.as_ptr(),
232                buf_idx as libbpf_sys::size_t,
233            )
234        };
235        util::parse_ret(ret)
236    }
237
238    #[doc(alias = "perf_buffer__buffer_cnt")]
239    pub fn buffer_cnt(&self) -> usize {
240        unsafe { libbpf_sys::perf_buffer__buffer_cnt(self.ptr.as_ptr()) as usize }
241    }
242
243    #[doc(alias = "perf_buffer__buffer_fd")]
244    pub fn buffer_fd(&self, buf_idx: usize) -> Result<i32> {
245        let ret = unsafe {
246            libbpf_sys::perf_buffer__buffer_fd(self.ptr.as_ptr(), buf_idx as libbpf_sys::size_t)
247        };
248        util::parse_ret_i32(ret)
249    }
250}
251
252impl AsRawLibbpf for PerfBuffer<'_> {
253    type LibbpfType = libbpf_sys::perf_buffer;
254
255    /// Retrieve the underlying [`libbpf_sys::perf_buffer`].
256    fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
257        self.ptr
258    }
259}
260
261// SAFETY: `perf_buffer` objects can safely be polled from any thread.
262unsafe impl Send for PerfBuffer<'_> {}
263
264impl Drop for PerfBuffer<'_> {
265    #[doc(alias = "perf_buffer__free")]
266    fn drop(&mut self) {
267        unsafe {
268            libbpf_sys::perf_buffer__free(self.ptr.as_ptr());
269        }
270    }
271}
272
273#[cfg(test)]
274mod test {
275    use super::*;
276
277    /// Check that `PerfBuffer` is `Send`.
278    #[test]
279    fn perfbuffer_is_send() {
280        fn test<T>()
281        where
282            T: Send,
283        {
284        }
285
286        test::<PerfBuffer<'_>>();
287    }
288}