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