perf_event_open/sample/mod.rs
1use std::fs::File;
2use std::io::Result;
3use std::sync::atomic::{AtomicU64, Ordering};
4use std::sync::Arc;
5
6use arena::Arena;
7use auxiliary::AuxTracer;
8use iter::{CowIter, Iter};
9use rb::Rb;
10use record::{Parser, UnsafeParser};
11
12use crate::count::Counter;
13use crate::ffi::syscall::ioctl_arg;
14use crate::ffi::{bindings as b, Metadata, PAGE_SIZE};
15
16mod arena;
17pub mod auxiliary;
18pub mod iter;
19pub mod rb;
20pub mod record;
21
22/// Event sampler.
23///
24/// This type provides the event sampling function of `perf_event_open`,
25/// which can capture the context when the event happens, helpling us to
26/// gain in-depth understanding of the system status at that time,
27/// similar to the `perf record` command.
28///
29/// # Examples
30///
31/// ```rust
32/// use std::thread;
33/// use std::time::Duration;
34///
35/// use perf_event_open::config::{Cpu, Opts, Proc, Size};
36/// use perf_event_open::count::Counter;
37/// use perf_event_open::event::hw::Hardware;
38/// # use perf_event_open::sample::record::Record;
39///
40/// // Count retired instructions on any process, CPU 0.
41/// let event = Hardware::Instr;
42/// let target = (Proc::ALL, Cpu(0));
43///
44/// let mut opts = Opts::default();
45/// opts.sample_format.user_stack = Some(Size(32)); // Dump 32-bytes user stack.
46///
47/// let counter = Counter::new(event, target, opts).unwrap();
48/// let sampler = counter.sampler(10).unwrap(); // Allocate 2^10 pages to store samples.
49///
50/// counter.enable().unwrap();
51/// thread::sleep(Duration::from_millis(10));
52/// counter.disable().unwrap();
53///
54/// for it in sampler.iter() {
55/// println!("{:-?}", it);
56/// # if let (_, Record::Sample(s)) = it {
57/// # assert!(s.user_stack.is_some());
58/// # }
59/// }
60/// ```
61pub struct Sampler {
62 perf: Arc<File>,
63 arena: Arena,
64 parser: Parser,
65}
66
67impl Sampler {
68 pub(super) fn new(counter: &Counter, exp: u8) -> Result<Self> {
69 let len = (1 + 2_usize.pow(exp as _)) * *PAGE_SIZE;
70 let arena = Arena::new(&counter.perf, len, 0)?;
71
72 // We only change the attr fields related to event config,
73 // which are not used in `ChunkParser::from_attr`.
74 let attr = unsafe { &*counter.attr.get() };
75 let parser = Parser(UnsafeParser::from_attr(attr));
76
77 Ok(Sampler {
78 perf: Arc::clone(&counter.perf),
79 arena,
80 parser,
81 })
82 }
83
84 /// Returns a record iterator over the kernel ring-buffer.
85 pub fn iter(&self) -> Iter<'_> {
86 let alloc = self.arena.as_slice();
87 let metadata = unsafe { &mut *(alloc.as_ptr() as *mut Metadata) };
88 let rb = Rb::new(
89 // https://github.com/torvalds/linux/blob/v6.13/kernel/events/core.c#L6212
90 &alloc[*PAGE_SIZE..],
91 unsafe { AtomicU64::from_ptr(&mut metadata.data_tail as _) },
92 unsafe { AtomicU64::from_ptr(&mut metadata.data_head as _) },
93 );
94 Iter(CowIter {
95 rb,
96 perf: &self.perf,
97 parser: &self.parser,
98 })
99 }
100
101 /// Record parser of the sampler.
102 pub fn parser(&self) -> &UnsafeParser {
103 &self.parser.0
104 }
105
106 /// Create an AUX tracer for this sampler.
107 ///
108 /// The AUX tracer needs a ring-buffer to store data,
109 /// and 1 + 2^`exp` pages will be allocated for this.
110 ///
111 /// Multiple calls to this method just duplicates the existing AUX tracer,
112 /// AUX tracers from the same sampler shares the same ring-buffer in the
113 /// kernel space, so `exp` should be the same.
114 pub fn aux_tracer(&self, exp: u8) -> Result<AuxTracer<'_>> {
115 let alloc = self.arena.as_slice();
116 let metadata = unsafe { &mut *(alloc.as_ptr() as *mut Metadata) };
117 AuxTracer::new(&self.perf, metadata, exp)
118 }
119
120 /// Pause the ring-buffer output.
121 ///
122 /// A paused ring-buffer does not prevent generation of samples, but simply
123 /// discards them. The discarded samples are considered lost, and cause a
124 /// [`LostRecords`][record::lost::LostRecords] to be generated when possible.
125 ///
126 /// An overflow signal may still be triggered by the discarded sample even
127 /// though the ring-buffer remains empty.
128 ///
129 /// Since `linux-4.7`: <https://github.com/torvalds/linux/commit/86e7972f690c1017fd086cdfe53d8524e68c661c>
130 #[cfg(feature = "linux-4.7")]
131 pub fn pause(&self) -> Result<()> {
132 ioctl_arg(&self.perf, b::PERF_IOC_OP_PAUSE_OUTPUT as _, 1)?;
133 Ok(())
134 }
135
136 #[cfg(not(feature = "linux-4.7"))]
137 pub fn pause(&self) -> Result<()> {
138 crate::config::unsupported!()
139 }
140
141 /// Resume the ring-buffer output.
142 ///
143 /// Since `linux-4.7`: <https://github.com/torvalds/linux/commit/86e7972f690c1017fd086cdfe53d8524e68c661c>
144 #[cfg(feature = "linux-4.7")]
145 pub fn resume(&self) -> Result<()> {
146 ioctl_arg(&self.perf, b::PERF_IOC_OP_PAUSE_OUTPUT as _, 0)?;
147 Ok(())
148 }
149
150 #[cfg(not(feature = "linux-4.7"))]
151 pub fn resume(&self) -> Result<()> {
152 crate::config::unsupported!()
153 }
154
155 /// Enables the counter until the maximum number of samples has been generated.
156 ///
157 /// The counter will be disabled if `max_samples` is reached.
158 ///
159 /// # Examples
160 ///
161 /// ```rust
162 /// use std::{thread, time::Duration};
163 ///
164 /// use perf_event_open::config::{Cpu, Opts, Proc, SampleOn};
165 /// use perf_event_open::count::Counter;
166 /// use perf_event_open::event::sw::Software;
167 ///
168 /// let event = Software::TaskClock;
169 /// let target = (Proc::ALL, Cpu(0));
170 /// let mut opts = Opts::default();
171 /// opts.sample_on = SampleOn::Count(1_000_000); // 1ms
172 ///
173 /// let counter = Counter::new(event, target, opts).unwrap();
174 /// let sampler = counter.sampler(5).unwrap();
175 ///
176 /// sampler.enable_counter_with(10).unwrap();
177 /// thread::sleep(Duration::from_millis(20));
178 ///
179 /// assert_eq!(sampler.iter().count(), 10);
180 /// ```
181 ///
182 /// Furthermore, we can capture the overflow events by enabling I/O signaling from
183 /// the perf event fd.
184 ///
185 /// On each overflow, `POLL_IN` is indicated if `max_samples` has not been reached.
186 /// Otherwise, `POLL_HUP` is indicated.
187 ///
188 ///```rust
189 /// # // Fork to avoid signal handler conflicts.
190 /// # unsafe {
191 /// # let child = libc::fork();
192 /// # if child > 0 {
193 /// # let mut code = 0;
194 /// # libc::waitpid(child, &mut code as _, 0);
195 /// # assert_eq!(code, 0);
196 /// # return;
197 /// # }
198 /// # }
199 /// #
200 /// # unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) };
201 /// #
202 /// # let result = std::panic::catch_unwind(|| {
203 /// use perf_event_open::config::{Cpu, Opts, Proc, SampleOn};
204 /// use perf_event_open::count::Counter;
205 /// use perf_event_open::event::sw::Software;
206 /// use std::mem::MaybeUninit;
207 /// use std::os::fd::AsRawFd;
208 /// use std::ptr::null_mut;
209 /// use std::sync::atomic::AtomicBool;
210 /// use std::sync::atomic::Ordering as MemOrd;
211 ///
212 /// const MAX_SAMPLES: usize = 3;
213 ///
214 /// let event = Software::TaskClock;
215 /// let target = (Proc::CURRENT, Cpu::ALL);
216 /// let mut opts = Opts::default();
217 /// opts.sample_on = SampleOn::Count(1_000_000); // 1ms
218 ///
219 /// let counter = Counter::new(event, target, opts).unwrap();
220 ///
221 /// // Enable I/O signals from perf event fd to the current process.
222 /// let fd = counter.file().as_raw_fd();
223 /// unsafe {
224 /// libc::fcntl(fd, libc::F_SETFL, libc::O_ASYNC);
225 /// // The value of `F_SETSIG` is 10, and libc crate does not have
226 /// // that binding (same as `POLL_IN` and `POLL_HUP` below).
227 /// libc::fcntl(fd, 10, libc::SIGIO);
228 /// libc::fcntl(fd, libc::F_SETOWN, libc::getpid());
229 /// }
230 ///
231 /// static IN: AtomicBool = AtomicBool::new(false);
232 /// static HUP: AtomicBool = AtomicBool::new(false);
233 ///
234 /// fn handler(num: i32, info: *const libc::siginfo_t) {
235 /// assert_eq!(num, libc::SIGIO);
236 /// match unsafe { *info }.si_code {
237 /// 1 => IN.store(true, MemOrd::Relaxed), // POLL_IN
238 /// 6 => HUP.store(true, MemOrd::Relaxed), // POLL_HUP
239 /// _ => unreachable!(),
240 /// }
241 /// }
242 ///
243 /// let act = libc::sigaction {
244 /// sa_sigaction: handler as _,
245 /// sa_mask: unsafe { MaybeUninit::zeroed().assume_init() },
246 /// sa_flags: libc::SA_SIGINFO,
247 /// sa_restorer: None,
248 /// };
249 /// unsafe { libc::sigaction(libc::SIGIO, &act as _, null_mut()) };
250 ///
251 /// let sampler = counter.sampler(5).unwrap();
252 /// sampler.enable_counter_with(MAX_SAMPLES as _).unwrap();
253 ///
254 /// let iter = &mut sampler.iter();
255 /// let mut count = 0;
256 /// while !HUP.load(MemOrd::Relaxed) {
257 /// while IN.swap(false, MemOrd::Relaxed) {
258 /// count += iter.count();
259 /// }
260 /// }
261 /// count += iter.count();
262 /// assert_eq!(count, MAX_SAMPLES);
263 /// # });
264 /// # if result.is_err() {
265 /// # unsafe { libc::abort() };
266 /// # }
267 /// #
268 /// # unsafe { libc::exit(0) };
269 /// ```
270 pub fn enable_counter_with(&self, max_samples: u32) -> Result<()> {
271 ioctl_arg(&self.perf, b::PERF_IOC_OP_REFRESH as _, max_samples as _)?;
272 Ok(())
273 }
274
275 /// Reset overflow condition.
276 ///
277 /// How to interpret `freq_or_count` depends on how the counter was created.
278 /// This means that the new frequency will be applied if the counter was
279 /// created with [`SampleOn::Freq`][crate::config::SampleOn], and so will the count.
280 pub fn sample_on(&self, freq_or_count: u64) -> Result<()> {
281 ioctl_arg(&self.perf, b::PERF_IOC_OP_PERIOD as _, freq_or_count)?;
282 Ok(())
283 }
284
285 fn metadata_inner(&self) -> *mut Metadata {
286 let alloc_ptr = self.arena.as_slice().as_ptr();
287 alloc_ptr as *mut Metadata
288 }
289
290 /// Counter's enabled time.
291 ///
292 /// Same as [time][crate::count::Stat::time_enabled] returned by [`Counter::stat`],
293 /// but much cheaper since the value is read from memory instead of system call.
294 pub fn counter_time_enabled(&self) -> u64 {
295 let metadata = self.metadata_inner();
296 let metadata = unsafe { &mut *metadata };
297 let time_enabled = unsafe { AtomicU64::from_ptr(&mut metadata.time_enabled as _) };
298 time_enabled.load(Ordering::Relaxed)
299 }
300
301 /// Counter's running time.
302 ///
303 /// Same as [time][crate::count::Stat::time_running] returned by [`Counter::stat`],
304 /// but much cheaper since the value is read from memory instead of system call.
305 pub fn counter_time_running(&self) -> u64 {
306 let metadata = self.metadata_inner();
307 let metadata = unsafe { &mut *metadata };
308 let time_running = unsafe { AtomicU64::from_ptr(&mut metadata.time_running as _) };
309 time_running.load(Ordering::Relaxed)
310 }
311}
312
313// `Arena::ptr` is valid during the lifetime of `Sampler`.
314unsafe impl Send for Sampler {}