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