• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use core::ffi::c_void;
2 use std::fmt::Debug;
3 use std::fmt::Formatter;
4 use std::fmt::Result as FmtResult;
5 use std::os::unix::prelude::AsRawFd;
6 use std::ptr;
7 use std::ptr::NonNull;
8 use std::slice;
9 use std::time::Duration;
10 
11 use crate::util;
12 use crate::util::validate_bpf_ret;
13 use crate::AsRawLibbpf;
14 use crate::Error;
15 use crate::ErrorExt as _;
16 use crate::MapCore;
17 use crate::MapType;
18 use crate::Result;
19 
20 type SampleCb<'b> = Box<dyn FnMut(i32, &[u8]) + 'b>;
21 type LostCb<'b> = Box<dyn FnMut(i32, u64) + 'b>;
22 
23 struct CbStruct<'b> {
24     sample_cb: Option<SampleCb<'b>>,
25     lost_cb: Option<LostCb<'b>>,
26 }
27 
28 impl Debug for CbStruct<'_> {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult29     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.
39 pub struct PerfBufferBuilder<'a, 'b, M>
40 where
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 
49 impl<'a, M> PerfBufferBuilder<'a, '_, M>
50 where
51     M: MapCore,
52 {
53     /// Create a new `PerfBufferBuilder` using the provided `MapCore`
54     /// object.
new(map: &'a M) -> Self55     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 
65 impl<'a, 'b, M> PerfBufferBuilder<'a, 'b, M>
66 where
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)`.
sample_cb<F>(self, cb: F) -> PerfBufferBuilder<'a, 'b, M> where F: FnMut(i32, &[u8]) + 'b,75     pub fn sample_cb<F>(self, cb: F) -> PerfBufferBuilder<'a, 'b, M>
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)`.
lost_cb<F>(self, cb: F) -> PerfBufferBuilder<'a, 'b, M> where F: FnMut(i32, u64) + 'b,90     pub fn lost_cb<F>(self, cb: F) -> PerfBufferBuilder<'a, 'b, M>
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.
pages(self, pages: usize) -> PerfBufferBuilder<'a, 'b, M>103     pub fn pages(self, pages: usize) -> PerfBufferBuilder<'a, 'b, M> {
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.
build(self) -> Result<PerfBuffer<'b>>113     pub fn build(self) -> Result<PerfBuffer<'b>> {
114         if self.map.map_type() != MapType::PerfEventArray {
115             return Err(Error::with_invalid_data("Must use a PerfEventArray map"));
116         }
117 
118         if !self.pages.is_power_of_two() {
119             return Err(Error::with_invalid_data("Page count must be power of two"));
120         }
121 
122         let c_sample_cb: libbpf_sys::perf_buffer_sample_fn = if self.sample_cb.is_some() {
123             Some(Self::call_sample_cb)
124         } else {
125             None
126         };
127 
128         let c_lost_cb: libbpf_sys::perf_buffer_lost_fn = if self.lost_cb.is_some() {
129             Some(Self::call_lost_cb)
130         } else {
131             None
132         };
133 
134         let callback_struct_ptr = Box::into_raw(Box::new(CbStruct {
135             sample_cb: self.sample_cb,
136             lost_cb: self.lost_cb,
137         }));
138 
139         let ptr = unsafe {
140             libbpf_sys::perf_buffer__new(
141                 self.map.as_fd().as_raw_fd(),
142                 self.pages as libbpf_sys::size_t,
143                 c_sample_cb,
144                 c_lost_cb,
145                 callback_struct_ptr as *mut _,
146                 ptr::null(),
147             )
148         };
149         let ptr = validate_bpf_ret(ptr).context("failed to create perf buffer")?;
150         let pb = PerfBuffer {
151             ptr,
152             _cb_struct: unsafe { Box::from_raw(callback_struct_ptr) },
153         };
154         Ok(pb)
155     }
156 
call_sample_cb(ctx: *mut c_void, cpu: i32, data: *mut c_void, size: u32)157     unsafe extern "C" fn call_sample_cb(ctx: *mut c_void, cpu: i32, data: *mut c_void, size: u32) {
158         let callback_struct = ctx as *mut CbStruct<'_>;
159 
160         if let Some(cb) = unsafe { &mut (*callback_struct).sample_cb } {
161             let slice = unsafe { slice::from_raw_parts(data as *const u8, size as usize) };
162             cb(cpu, slice);
163         }
164     }
165 
call_lost_cb(ctx: *mut c_void, cpu: i32, count: u64)166     unsafe extern "C" fn call_lost_cb(ctx: *mut c_void, cpu: i32, count: u64) {
167         let callback_struct = ctx as *mut CbStruct<'_>;
168 
169         if let Some(cb) = unsafe { &mut (*callback_struct).lost_cb } {
170             cb(cpu, count);
171         }
172     }
173 }
174 
175 impl<M> Debug for PerfBufferBuilder<'_, '_, M>
176 where
177     M: MapCore,
178 {
fmt(&self, f: &mut Formatter<'_>) -> FmtResult179     fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
180         let Self {
181             map,
182             pages,
183             sample_cb,
184             lost_cb,
185         } = self;
186         f.debug_struct("PerfBufferBuilder")
187             .field("map", map)
188             .field("pages", pages)
189             .field("sample_cb", &sample_cb.as_ref().map(|cb| &cb as *const _))
190             .field("lost_cb", &lost_cb.as_ref().map(|cb| &cb as *const _))
191             .finish()
192     }
193 }
194 
195 /// Represents a special kind of [`MapCore`]. Typically used to transfer data between
196 /// [`Program`][crate::Program]s and userspace.
197 #[derive(Debug)]
198 pub struct PerfBuffer<'b> {
199     ptr: NonNull<libbpf_sys::perf_buffer>,
200     // Hold onto the box so it'll get dropped when PerfBuffer is dropped
201     _cb_struct: Box<CbStruct<'b>>,
202 }
203 
204 // TODO: Document methods.
205 #[allow(missing_docs)]
206 impl PerfBuffer<'_> {
epoll_fd(&self) -> i32207     pub fn epoll_fd(&self) -> i32 {
208         unsafe { libbpf_sys::perf_buffer__epoll_fd(self.ptr.as_ptr()) }
209     }
210 
poll(&self, timeout: Duration) -> Result<()>211     pub fn poll(&self, timeout: Duration) -> Result<()> {
212         let ret =
213             unsafe { libbpf_sys::perf_buffer__poll(self.ptr.as_ptr(), timeout.as_millis() as i32) };
214         util::parse_ret(ret)
215     }
216 
consume(&self) -> Result<()>217     pub fn consume(&self) -> Result<()> {
218         let ret = unsafe { libbpf_sys::perf_buffer__consume(self.ptr.as_ptr()) };
219         util::parse_ret(ret)
220     }
221 
consume_buffer(&self, buf_idx: usize) -> Result<()>222     pub fn consume_buffer(&self, buf_idx: usize) -> Result<()> {
223         let ret = unsafe {
224             libbpf_sys::perf_buffer__consume_buffer(
225                 self.ptr.as_ptr(),
226                 buf_idx as libbpf_sys::size_t,
227             )
228         };
229         util::parse_ret(ret)
230     }
231 
buffer_cnt(&self) -> usize232     pub fn buffer_cnt(&self) -> usize {
233         unsafe { libbpf_sys::perf_buffer__buffer_cnt(self.ptr.as_ptr()) as usize }
234     }
235 
buffer_fd(&self, buf_idx: usize) -> Result<i32>236     pub fn buffer_fd(&self, buf_idx: usize) -> Result<i32> {
237         let ret = unsafe {
238             libbpf_sys::perf_buffer__buffer_fd(self.ptr.as_ptr(), buf_idx as libbpf_sys::size_t)
239         };
240         util::parse_ret_i32(ret)
241     }
242 }
243 
244 impl AsRawLibbpf for PerfBuffer<'_> {
245     type LibbpfType = libbpf_sys::perf_buffer;
246 
247     /// Retrieve the underlying [`libbpf_sys::perf_buffer`].
as_libbpf_object(&self) -> NonNull<Self::LibbpfType>248     fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
249         self.ptr
250     }
251 }
252 
253 // SAFETY: `perf_buffer` objects can safely be polled from any thread.
254 unsafe impl Send for PerfBuffer<'_> {}
255 
256 impl Drop for PerfBuffer<'_> {
drop(&mut self)257     fn drop(&mut self) {
258         unsafe {
259             libbpf_sys::perf_buffer__free(self.ptr.as_ptr());
260         }
261     }
262 }
263 
264 #[cfg(test)]
265 mod test {
266     use super::*;
267 
268     /// Check that `PerfBuffer` is `Send`.
269     #[test]
perfbuffer_is_send()270     fn perfbuffer_is_send() {
271         fn test<T>()
272         where
273             T: Send,
274         {
275         }
276 
277         test::<PerfBuffer<'_>>();
278     }
279 }
280