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
38pub 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 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 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 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 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 #[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#[derive(Debug)]
199#[doc(alias = "perf_buffer")]
200pub struct PerfBuffer<'b> {
201 ptr: NonNull<libbpf_sys::perf_buffer>,
202 _cb_struct: Box<CbStruct<'b>>,
204}
205
206#[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 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
257 self.ptr
258 }
259}
260
261unsafe 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 #[test]
279 fn perfbuffer_is_send() {
280 fn test<T>()
281 where
282 T: Send,
283 {
284 }
285
286 test::<PerfBuffer<'_>>();
287 }
288}