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) -> 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 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 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 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
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
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
175impl<M> Debug for PerfBufferBuilder<'_, '_, M>
176where
177 M: MapCore,
178{
179 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#[derive(Debug)]
198pub struct PerfBuffer<'b> {
199 ptr: NonNull<libbpf_sys::perf_buffer>,
200 _cb_struct: Box<CbStruct<'b>>,
202}
203
204#[allow(missing_docs)]
206impl PerfBuffer<'_> {
207 pub fn epoll_fd(&self) -> i32 {
208 unsafe { libbpf_sys::perf_buffer__epoll_fd(self.ptr.as_ptr()) }
209 }
210
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
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
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
232 pub fn buffer_cnt(&self) -> usize {
233 unsafe { libbpf_sys::perf_buffer__buffer_cnt(self.ptr.as_ptr()) as usize }
234 }
235
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
244impl AsRawLibbpf for PerfBuffer<'_> {
245 type LibbpfType = libbpf_sys::perf_buffer;
246
247 fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
249 self.ptr
250 }
251}
252
253unsafe impl Send for PerfBuffer<'_> {}
255
256impl Drop for PerfBuffer<'_> {
257 fn drop(&mut self) {
258 unsafe {
259 libbpf_sys::perf_buffer__free(self.ptr.as_ptr());
260 }
261 }
262}
263
264#[cfg(test)]
265mod test {
266 use super::*;
267
268 #[test]
270 fn perfbuffer_is_send() {
271 fn test<T>()
272 where
273 T: Send,
274 {
275 }
276
277 test::<PerfBuffer<'_>>();
278 }
279}