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