perf_event/sampler.rs
1use std::borrow::Cow;
2use std::convert::{AsMut, AsRef};
3use std::ops::{Deref, DerefMut};
4use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
5use std::sync::atomic::Ordering;
6use std::time::{Duration, Instant};
7
8use crate::data::parse::{ParseBuf, ParseBufChunk, ParseError, ParseResult, Parser};
9use crate::events::Hardware;
10use crate::sys::bindings::{
11 __BindgenBitfieldUnit, perf_event_header, perf_event_mmap_page,
12 perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1 as MmapPageFlags,
13};
14use crate::{check_errno_syscall, data, Counter};
15
16used_in_docs!(Hardware);
17
18/// A sampled perf event.
19///
20/// A sampler for a sampler perf event consists of two things: a [`Counter`],
21/// and a memory-mapped ring buffer into which the kernel periodically writes
22/// events. The specific event is configured on construction and can vary from
23/// changes to the memory mapping associated with a process, to sampling call
24/// stacks, to getting the output from a bpf program running in the kernel, and
25/// more.
26///
27/// This sampler type provides direct access to the bytes written by the kernel
28/// without doing any parsing of the emitted records. To actually read the
29/// involved fields you will need to parse them yourself. See the
30/// [`perf_event_open` man page][0] for documentation on how the sample records
31/// are represented in memory.
32///
33/// [0]: https://www.mankier.com/2/perf_event_open
34pub struct Sampler {
35 counter: Counter,
36 mmap: memmap2::MmapRaw,
37}
38
39/// A view into a [`Sampler`]'s ring buffer for a single kernel event record.
40///
41/// When dropped, this type will advance the tail pointer in the ringbuffer of
42/// the [`Sampler`] that it references. To avoid this, you can use
43/// [`std::mem::forget`] so the next call to [`Sampler::next_record`] will
44/// return the same record again.
45pub struct Record<'a> {
46 sampler: &'a Sampler,
47 header: perf_event_header,
48 data: ByteBuffer<'a>,
49}
50
51/// A `Buf` that can be either a single byte slice or two disjoint byte
52/// slices.
53#[derive(Copy, Clone)]
54enum ByteBuffer<'a> {
55 Single(&'a [u8]),
56 Split([&'a [u8]; 2]),
57}
58
59impl Sampler {
60 pub(crate) fn new(counter: Counter, mmap: memmap2::MmapRaw) -> Self {
61 assert!(!mmap.as_ptr().is_null());
62
63 Self { counter, mmap }
64 }
65
66 /// Convert this sampler back into a counter.
67 ///
68 /// This will close the ringbuffer associated with the sampler.
69 pub fn into_counter(self) -> Counter {
70 self.counter
71 }
72
73 /// Access the underlying counter for this sampler.
74 pub fn as_counter(&self) -> &Counter {
75 &self.counter
76 }
77
78 /// Mutably access the underlying counter for this sampler.
79 pub fn as_counter_mut(&mut self) -> &mut Counter {
80 &mut self.counter
81 }
82
83 /// Read the next record from the ring buffer.
84 ///
85 /// This method does not block. If you want blocking behaviour, use
86 /// [`next_blocking`] instead.
87 ///
88 /// It is possible to get readiness notifications for when events are
89 /// present in the ring buffer (e.g. for async code). See the documentation
90 /// on the [`perf_event_open`][man] manpage for details on how to do this.
91 ///
92 /// [`next_blocking`]: Self::next_blocking
93 /// [man]: https://www.mankier.com/2/perf_event_open
94 pub fn next_record(&mut self) -> Option<Record> {
95 use std::{mem, ptr, slice};
96
97 let page = self.page();
98
99 // SAFETY:
100 // - page points to a valid instance of perf_event_mmap_page.
101 // - data_tail is only written by the user side so it is safe to do a non-atomic
102 // read here.
103 let tail = unsafe { ptr::read(ptr::addr_of!((*page).data_tail)) };
104 // ATOMICS:
105 // - The acquire load here syncronizes with the release store in the kernel and
106 // ensures that all the data written to the ring buffer before data_head is
107 // visible to this thread.
108 // SAFETY:
109 // - page points to a valid instance of perf_event_mmap_page.
110 let head = unsafe { atomic_load(ptr::addr_of!((*page).data_head), Ordering::Acquire) };
111
112 if tail == head {
113 return None;
114 }
115
116 // SAFETY: (for both statements)
117 // - page points to a valid instance of perf_event_mmap_page.
118 // - neither of these fields are written to except before the map is created so
119 // reading from them non-atomically is safe.
120 let data_size = unsafe { ptr::read(ptr::addr_of!((*page).data_size)) };
121 let data_offset = unsafe { ptr::read(ptr::addr_of!((*page).data_offset)) };
122
123 let mod_tail = (tail % data_size) as usize;
124 let mod_head = (head % data_size) as usize;
125
126 // SAFETY:
127 // - perf_event_open guarantees that page.data_offset is within the memory
128 // mapping.
129 let data_start = unsafe { self.mmap.as_ptr().add(data_offset as usize) };
130 // SAFETY:
131 // - data_start is guaranteed to be valid for at least data_size bytes.
132 let tail_start = unsafe { data_start.add(mod_tail) };
133
134 let mut buffer = if mod_head > mod_tail {
135 ByteBuffer::Single(unsafe { slice::from_raw_parts(tail_start, mod_head - mod_tail) })
136 } else {
137 ByteBuffer::Split([
138 unsafe { slice::from_raw_parts(tail_start, data_size as usize - mod_tail) },
139 unsafe { slice::from_raw_parts(data_start, mod_head) },
140 ])
141 };
142
143 let header = buffer.parse_header();
144 assert!(header.size as usize >= mem::size_of::<perf_event_header>());
145 buffer.truncate(header.size as usize - mem::size_of::<perf_event_header>());
146
147 Some(Record {
148 sampler: self,
149 header,
150 data: buffer,
151 })
152 }
153
154 /// Read the next record from the ring buffer. This method will block (with
155 /// an optional timeout) until a new record is available.
156 ///
157 /// If this sampler is only enabled for a single process and that process
158 /// exits, this method will return `None` even if no timeout is passed.
159 /// Note that this only works on Linux 3.18 and above.
160 ///
161 /// # Panics
162 /// This method will panic if an unexpected error is returned from
163 /// `libc::poll`. There are only two cases where this can happen:
164 /// - the current process has run out of file descriptors, or,
165 /// - the kernel couldn't allocate memory for internal poll datastructures.
166 pub fn next_blocking(&mut self, timeout: Option<Duration>) -> Option<Record> {
167 let deadline = timeout.map(|timeout| Instant::now() + timeout);
168
169 loop {
170 if let Some(record) = self.next_record() {
171 // This is a workaround for a known limitation of NLL in rustc.
172 // If it worked, we could do
173 // return Some(record);
174 // but currently that extends the lifetime for the &mut self
175 // borrow to cover the whole function and that causes conflicts
176 // with other borrows further down.
177 //
178 // Fixing this is tracked in the following rustc issue
179 // https://github.com/rust-lang/rust/issues/51132
180 //
181 // You can verify that the code above should, in fact, pass the
182 // borrow checker by removing the line below, uncommenting the
183 // line above, and checking it via
184 // cargo +nightly rustc -- -Zpolonius
185 return Some(unsafe { std::mem::transmute::<Record, Record>(record) });
186 }
187
188 let timeout = match deadline {
189 Some(deadline) => deadline
190 .checked_duration_since(Instant::now())?
191 .as_millis()
192 .min(libc::c_int::MAX as u128) as libc::c_int,
193 None => -1,
194 };
195
196 let mut pollfd = libc::pollfd {
197 fd: self.as_raw_fd(),
198 events: libc::POLLIN,
199 revents: 0,
200 };
201
202 match check_errno_syscall(|| unsafe { libc::poll(&mut pollfd, 1, timeout) }) {
203 // poll timed out.
204 Ok(0) => return None,
205 // The sampler was tracking a single other process and that
206 // process has exited.
207 //
208 // However, there may still be events in the ring buffer in this case so
209 // we still need to check.
210 Ok(_) if pollfd.revents & libc::POLLHUP != 0 => return self.next_record(),
211 // Must be POLLIN, there should be an event ready.
212 Ok(_) => continue,
213 Err(e) => match e.raw_os_error() {
214 Some(libc::EINTR) => continue,
215 // The only other possible kernel errors here are so rare
216 // that it doesn't make sense to make this API have a
217 // result because of them. To whit, they are:
218 // - EINVAL - the process ran out of file descriptors
219 // - ENOMEM - the kernel couldn't allocate memory for the poll datastructures.
220 // In this case, we panic.
221 _ => panic!(
222 "polling a perf-event fd returned an unexpected error: {}",
223 e
224 ),
225 },
226 }
227 }
228 }
229
230 /// Read the value of this counter directly from userspace.
231 ///
232 /// Some CPU architectures allow performance counters to be read directly
233 /// from userspace without having to go through the kernel. This can be much
234 /// faster than a normal counter read but the tradeoff is that can only be
235 /// done under certain conditions.
236 ///
237 /// This method allows you to read the counter value, `time_enabled`, and
238 /// `time_running` without going through the kernel, if allowed by the
239 /// combination of architecture, kernel, and counter. `time_enabled` and
240 /// `time_running` are always read but will be less accurate on
241 /// architectures that do not provide a timestamp counter readable from
242 /// userspace.
243 ///
244 /// # Restrictions
245 /// In order for counter values to be read using this method the following
246 /// must be true:
247 /// - the CPU architecture must support reading counters from userspace,
248 /// - the counter must be recording for the current process,
249 /// - perf-event2 must have support for the relevant CPU architecture, and,
250 /// - the counter must correspond to a hardware counter.
251 ///
252 /// Note that, despite the above being true, the kernel may still not
253 /// support userspace reads for other reasons. [`Hardware`] events should
254 /// usually be supported but anything beyond that is unlikely. See the
255 /// supported architectures table below to see which are supported by
256 /// perf-event2.
257 ///
258 /// Accurate timestamps also require that the kernel, CPU, and perf-event2
259 /// support them. They have similar restrictions to counter reads and will
260 /// just return the base values set by the kernel otherwise. These will may
261 /// be somewhat accurate but are likely to be out-of-date.
262 ///
263 /// # Supported Architectures
264 /// | Architecture | Counter Read | Timestamp Read |
265 /// |--------------|--------------|----------------|
266 /// | x86/x86_64 | yes | yes |
267 ///
268 /// If you would like to add support for a new architecture here please
269 /// submit a PR!
270 pub fn read_user(&self) -> UserReadData {
271 #[cfg(target_arch = "x86")]
272 use std::arch::x86::_rdtsc;
273 #[cfg(target_arch = "x86_64")]
274 use std::arch::x86_64::_rdtsc;
275
276 loop {
277 let mut data = unsafe { PmcReadData::new(self.page()) };
278
279 if let Some(index) = data.index() {
280 // SAFETY:
281 // - index was handed to us by the kernel so it is safe to use.
282 // - cap_user_rdpmc will only be set if it is valid to call rdpmc from
283 // userspace.
284 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
285 data.with_pmc(unsafe { rdpmc(index) });
286 }
287
288 if data.cap_user_time() {
289 // SAFETY: it is always safe to run rdtsc on x86
290 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
291 data.with_tsc(unsafe { _rdtsc() });
292 }
293
294 if let Some(data) = data.finish() {
295 return data;
296 }
297 }
298 }
299
300 fn page(&self) -> *const perf_event_mmap_page {
301 self.mmap.as_ptr() as *const _
302 }
303}
304
305impl Deref for Sampler {
306 type Target = Counter;
307
308 fn deref(&self) -> &Self::Target {
309 &self.counter
310 }
311}
312
313impl DerefMut for Sampler {
314 fn deref_mut(&mut self) -> &mut Self::Target {
315 &mut self.counter
316 }
317}
318
319impl AsRef<Counter> for Sampler {
320 fn as_ref(&self) -> &Counter {
321 &self.counter
322 }
323}
324
325impl AsMut<Counter> for Sampler {
326 fn as_mut(&mut self) -> &mut Counter {
327 &mut self.counter
328 }
329}
330
331impl AsRawFd for Sampler {
332 fn as_raw_fd(&self) -> RawFd {
333 self.counter.as_raw_fd()
334 }
335}
336
337impl IntoRawFd for Sampler {
338 fn into_raw_fd(self) -> RawFd {
339 self.counter.into_raw_fd()
340 }
341}
342
343// This is meant to roughly be the equivalent of the kernel READ_ONCE
344// macro. The closest equivalent in Rust (and, I think, the only one
345// that avoids UB) is to do a relaxed atomic load.
346//
347// On x86 this translates to just a load from memory and it is still
348// marked as an atomic for the compiler.
349macro_rules! read_once {
350 ($place:expr) => {
351 atomic_load(::std::ptr::addr_of!($place), Ordering::Relaxed)
352 };
353}
354
355// This is meant to be the equivalent of the kernel barrier macro. It prevents
356// the compiler from reordering any memory accesses accross the barrier.
357macro_rules! barrier {
358 () => {
359 std::sync::atomic::compiler_fence(Ordering::SeqCst)
360 };
361}
362
363/// Helper for writing a `read_user` variant.
364struct PmcReadData {
365 page: *const perf_event_mmap_page,
366 seq: u32,
367 flags: MmapPageFlags,
368 enabled: u64,
369 running: u64,
370 index: u32,
371 count: i64,
372
373 /// There are architectures that perf supports that we don't. On those
374 /// architectures we don't want to just return the offset value from the
375 /// perf mmap page.
376 has_pmc_value: bool,
377}
378
379#[allow(dead_code)]
380impl PmcReadData {
381 /// Read the initial sequence number and other values out of the page.
382 ///
383 /// # Safety
384 /// - `page` must point to a valid instance of [`perf_event_mmap_page`]
385 /// - `page` must remain valid for the lifetime of this struct.
386 pub unsafe fn new(page: *const perf_event_mmap_page) -> Self {
387 let seq = atomic_load(std::ptr::addr_of!((*page).lock), Ordering::Acquire);
388 barrier!();
389
390 let capabilities = read_once!((*page).__bindgen_anon_1.capabilities);
391
392 Self {
393 page,
394 seq,
395 flags: {
396 let mut flags = MmapPageFlags::default();
397 flags._bitfield_1 = __BindgenBitfieldUnit::new(capabilities.to_ne_bytes());
398 flags
399 },
400 enabled: read_once!((*page).time_enabled),
401 running: read_once!((*page).time_running),
402
403 index: read_once!((*page).index),
404 count: read_once!((*page).offset),
405 has_pmc_value: false,
406 }
407 }
408
409 pub fn cap_user_rdpmc(&self) -> bool {
410 self.flags.cap_user_rdpmc() != 0
411 }
412
413 pub fn cap_user_time(&self) -> bool {
414 self.flags.cap_user_time() != 0
415 }
416
417 pub fn cap_user_time_short(&self) -> bool {
418 self.flags.cap_user_time_short() != 0
419 }
420
421 /// Get the index of the PMC counter, should there be one to read.
422 pub fn index(&self) -> Option<u32> {
423 if self.cap_user_rdpmc() && self.index != 0 {
424 Some(self.index - 1)
425 } else {
426 None
427 }
428 }
429
430 /// Update the `enabled` and `running` counts using the tsc counter.
431 ///
432 /// # Panics
433 /// Panics if `cap_user_time` is not true.
434 pub fn with_tsc(&mut self, mut cyc: u64) {
435 assert!(self.cap_user_time());
436
437 // counter is not active so enabled and running should be accurate.
438 if !self.cap_user_rdpmc() || self.index == 0 {
439 return;
440 }
441
442 let page = self.page;
443
444 let time_offset = unsafe { read_once!((*page).time_offset) };
445 let time_mult = unsafe { read_once!((*page).time_mult) };
446 let time_shift = unsafe { read_once!((*page).time_shift) };
447
448 if self.cap_user_time_short() {
449 let time_cycles = unsafe { read_once!((*page).time_cycles) };
450 let time_mask = unsafe { read_once!((*page).time_mask) };
451
452 cyc = time_cycles + ((cyc - time_cycles) & time_mask);
453 }
454
455 let time_mult = time_mult as u64;
456 let quot = cyc >> time_shift;
457 let rem = cyc & ((1u64 << time_shift) - 1);
458
459 let delta = quot * time_mult + ((rem * time_mult) >> time_shift);
460 let delta = time_offset.wrapping_add(delta);
461
462 self.enabled += delta;
463 if self.index != 0 {
464 self.running += delta;
465 }
466 }
467
468 /// Update the value of `count` using a value read from the architecture
469 /// PMC.
470 ///
471 /// # Panics
472 /// Panics if `index` return `None`.
473 pub fn with_pmc(&mut self, pmc: u64) {
474 assert!(self.index().is_some());
475
476 let Self { page, .. } = *self;
477 let width = unsafe { read_once!((*page).pmc_width) };
478
479 let mut pmc = pmc as i64;
480 pmc <<= 64 - width;
481 pmc >>= 64 - width;
482
483 self.count = self.count.wrapping_add(pmc);
484 self.has_pmc_value = true;
485 }
486
487 pub fn finish(self) -> Option<UserReadData> {
488 let page = self.page;
489 let seq = self.seq;
490
491 barrier!();
492 let nseq = unsafe { atomic_load(std::ptr::addr_of!((*page).lock), Ordering::Acquire) };
493 if nseq != seq {
494 return None;
495 }
496
497 Some(UserReadData {
498 time_enabled: self.enabled,
499 time_running: self.running,
500 value: if self.has_pmc_value {
501 Some(self.count as u64)
502 } else {
503 None
504 },
505 })
506 }
507}
508
509/// Data read from a call to [`Sampler::read_user`].
510#[derive(Copy, Clone, Debug)]
511pub struct UserReadData {
512 time_enabled: u64,
513 time_running: u64,
514 value: Option<u64>,
515}
516
517impl UserReadData {
518 /// The total time for which the counter was enabled at the time of reading.
519 ///
520 /// If the architecture and counter support it this will be cycle-accurate
521 pub fn time_enabled(&self) -> Duration {
522 Duration::from_nanos(self.time_enabled)
523 }
524
525 /// The total time for which the counter was running at the time of reading.
526 pub fn time_running(&self) -> Duration {
527 Duration::from_nanos(self.time_running)
528 }
529
530 /// The value of the counter, if it was enabled at the time.
531 pub fn count(&self) -> Option<u64> {
532 self.value
533 }
534
535 /// The value of the counter, scaled to reflect `time_enabled`.
536 pub fn scaled_count(&self) -> Option<u64> {
537 self.count().map(|count| {
538 let quot = count / self.time_running;
539 let rem = count % self.time_running;
540 quot * self.time_enabled + (rem * self.time_enabled) / self.time_running
541 })
542 }
543}
544
545impl<'s> Record<'s> {
546 /// Access the `type` field of the kernel record header.
547 ///
548 /// This indicates the type of the record emitted by the kernel.
549 pub fn ty(&self) -> u32 {
550 self.header.type_
551 }
552
553 /// Access the `misc` field of the kernel record header.
554 ///
555 /// This contains a set of flags that carry some additional metadata on the
556 /// record being emitted by the kernel.
557 pub fn misc(&self) -> u16 {
558 self.header.misc
559 }
560
561 /// Get the total length, in bytes, of this record.
562 #[allow(clippy::len_without_is_empty)] // Records are never empty
563 pub fn len(&self) -> usize {
564 self.data.len()
565 }
566
567 /// Access the bytes of this record.
568 ///
569 /// Since the underlying buffer is a ring buffer the bytes of the record
570 /// may end up wrapping around the end of the buffer. That gets exposed
571 /// here as data returning either one or two byte slices. If there is no
572 /// wrap-around then one slice will be returned here, otherwise, two will
573 /// be returned.
574 pub fn data(&self) -> &[&[u8]] {
575 match &self.data {
576 ByteBuffer::Single(buf) => std::slice::from_ref(buf),
577 ByteBuffer::Split(bufs) => &bufs[..],
578 }
579 }
580
581 /// Copy the bytes of this record to an owned [`Vec`].
582 pub fn to_vec(&self) -> Vec<u8> {
583 self.to_contiguous().into_owned()
584 }
585
586 /// Get the bytes of this record as a single contiguous slice.
587 ///
588 /// For most records this is effectively free but if the record wraps
589 /// around the end of the ringbuffer then it will be copied to a vector.
590 pub fn to_contiguous(&self) -> Cow<[u8]> {
591 match self.data {
592 ByteBuffer::Single(data) => Cow::Borrowed(data),
593 ByteBuffer::Split([a, b]) => {
594 let mut vec = Vec::with_capacity(a.len() + b.len());
595 vec.extend_from_slice(a);
596 vec.extend_from_slice(b);
597 Cow::Owned(vec)
598 }
599 }
600 }
601
602 /// Parse the data in this record to a [`data::Record`] enum.
603 pub fn parse_record(&self) -> ParseResult<data::Record> {
604 let mut parser = Parser::new(self.data, self.sampler.config().clone());
605 data::Record::parse_with_header(&mut parser, self.header)
606 }
607}
608
609impl<'s> Drop for Record<'s> {
610 fn drop(&mut self) {
611 use std::ptr;
612
613 let page = self.sampler.page();
614
615 unsafe {
616 // SAFETY:
617 // - page points to a valid instance of perf_event_mmap_page
618 // - data_tail is only written on our side so it is safe to do a non-atomic read
619 // here.
620 let tail = ptr::read(ptr::addr_of!((*page).data_tail));
621
622 // ATOMICS:
623 // - The release store here prevents the compiler from re-ordering any reads
624 // past the store to data_tail.
625 // SAFETY:
626 // - page points to a valid instance of perf_event_mmap_page
627 atomic_store(
628 ptr::addr_of!((*page).data_tail),
629 tail + (self.header.size as u64),
630 Ordering::Release,
631 );
632 }
633 }
634}
635
636// Record contains a pointer which prevents it from implementing Send or Sync
637// by default. It is, however, valid to send it across threads and it has no
638// interior mutability so we implement Send and Sync here manually.
639unsafe impl<'s> Sync for Record<'s> {}
640unsafe impl<'s> Send for Record<'s> {}
641
642impl<'a> ByteBuffer<'a> {
643 /// Parse an instance of `perf_event_header` out of the start of this
644 /// byte buffer.
645 fn parse_header(&mut self) -> perf_event_header {
646 let mut bytes = [0; std::mem::size_of::<perf_event_header>()];
647 self.copy_to_slice(&mut bytes);
648 // SAFETY: perf_event_header is a packed C struct so it is valid to
649 // copy arbitrary initialized memory into it.
650 unsafe { std::mem::transmute(bytes) }
651 }
652
653 fn len(&self) -> usize {
654 match self {
655 Self::Single(buf) => buf.len(),
656 Self::Split([a, b]) => a.len() + b.len(),
657 }
658 }
659
660 /// Shorten this byte buffer to only include the first `new_len` bytes.
661 ///
662 /// # Panics
663 /// Panics if `new_len > self.len()`.
664 fn truncate(&mut self, new_len: usize) {
665 assert!(new_len <= self.len());
666
667 *self = match *self {
668 Self::Single(buf) => Self::Single(&buf[..new_len]),
669 Self::Split([a, b]) => {
670 if new_len <= a.len() {
671 Self::Single(&a[..new_len])
672 } else {
673 Self::Split([a, &b[..new_len - a.len()]])
674 }
675 }
676 }
677 }
678
679 /// Copy bytes from within this byte buffer to the provided slice.
680 ///
681 /// This will also remove those same bytes from the front of this byte
682 /// buffer.
683 ///
684 /// # Panics
685 /// Panics if `self.len() < dst.len()`
686 fn copy_to_slice(&mut self, dst: &mut [u8]) {
687 assert!(self.len() >= dst.len());
688
689 match self {
690 Self::Single(buf) => {
691 let (head, rest) = buf.split_at(dst.len());
692 dst.copy_from_slice(head);
693 *buf = rest;
694 }
695 Self::Split([buf, _]) if buf.len() >= dst.len() => {
696 let (head, rest) = buf.split_at(dst.len());
697 dst.copy_from_slice(head);
698 *buf = rest;
699 }
700 &mut Self::Split([a, b]) => {
701 let (d_head, d_rest) = dst.split_at_mut(a.len());
702 let (b_head, b_rest) = b.split_at(d_rest.len());
703
704 d_head.copy_from_slice(a);
705 d_rest.copy_from_slice(b_head);
706 *self = Self::Single(b_rest);
707 }
708 }
709 }
710}
711
712unsafe impl<'a> ParseBuf<'a> for ByteBuffer<'a> {
713 fn chunk(&mut self) -> ParseResult<ParseBufChunk<'_, 'a>> {
714 match self {
715 Self::Single([]) => Err(ParseError::eof()),
716 Self::Single(chunk) => Ok(ParseBufChunk::External(chunk)),
717 Self::Split([chunk, _]) => Ok(ParseBufChunk::External(chunk)),
718 }
719 }
720
721 fn advance(&mut self, mut count: usize) {
722 match self {
723 Self::Single(chunk) => chunk.advance(count),
724 Self::Split([chunk, _]) if count < chunk.len() => chunk.advance(count),
725 Self::Split([a, b]) => {
726 count -= a.len();
727 b.advance(count);
728 *self = Self::Single(b);
729 }
730 }
731 }
732}
733
734macro_rules! assert_same_size {
735 ($a:ty, $b:ty) => {{
736 if false {
737 let _assert_same_size: [u8; ::std::mem::size_of::<$b>()] =
738 [0u8; ::std::mem::size_of::<$a>()];
739 }
740 }};
741}
742
743trait Atomic: Sized + Copy {
744 type Atomic;
745
746 unsafe fn store(ptr: *const Self, val: Self, order: Ordering);
747 unsafe fn load(ptr: *const Self, order: Ordering) -> Self;
748}
749
750macro_rules! impl_atomic {
751 ($base:ty, $atomic:ty) => {
752 impl Atomic for $base {
753 type Atomic = $atomic;
754
755 unsafe fn store(ptr: *const Self, val: Self, order: Ordering) {
756 assert_same_size!(Self, Self::Atomic);
757
758 let ptr = ptr as *const Self::Atomic;
759 (*ptr).store(val, order)
760 }
761
762 unsafe fn load(ptr: *const Self, order: Ordering) -> Self {
763 assert_same_size!(Self, Self::Atomic);
764
765 let ptr = ptr as *const Self::Atomic;
766 (*ptr).load(order)
767 }
768 }
769 };
770}
771
772impl_atomic!(u64, std::sync::atomic::AtomicU64);
773impl_atomic!(u32, std::sync::atomic::AtomicU32);
774impl_atomic!(u16, std::sync::atomic::AtomicU16);
775impl_atomic!(i64, std::sync::atomic::AtomicI64);
776
777/// Do an atomic write to the value stored at `ptr`.
778///
779/// # Safety
780/// - `ptr` must be valid for writes.
781/// - `ptr` must be properly aligned.
782unsafe fn atomic_store<T: Atomic>(ptr: *const T, val: T, order: Ordering) {
783 T::store(ptr, val, order)
784}
785
786/// Perform an atomic read from the value stored at `ptr`.
787///
788/// # Safety
789/// - `ptr` must be valid for reads.
790/// - `ptr` must be properly aligned.
791unsafe fn atomic_load<T: Atomic>(ptr: *const T, order: Ordering) -> T {
792 T::load(ptr, order)
793}
794
795/// Read a performance monitoring counter via the `rdpmc` instruction.
796///
797/// # Safety
798/// - `index` must be a valid PMC index
799/// - The current CPU must be allowed to execute the `rdpmc` instruction at the
800/// current priviledge level.
801///
802/// Note that the safety constraints come from the x86 ISA so any violation of
803/// them will likely lead to a SIGINT or other such signal.
804#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
805unsafe fn rdpmc(index: u32) -> u64 {
806 // This saves a few instructions for 64-bit since LLVM doesn't realize
807 // that the top 32 bits of RAX:RDX are cleared otherwise.
808 #[cfg(target_arch = "x86_64")]
809 {
810 let lo: u64;
811 let hi: u64;
812
813 std::arch::asm!(
814 "rdpmc",
815 in("ecx") index,
816 out("rax") lo,
817 out("rdx") hi
818 );
819
820 lo | (hi << u32::BITS)
821 }
822
823 #[cfg(target_arch = "x86")]
824 {
825 let lo: u32;
826 let hi: u32;
827
828 std::arch::asm!(
829 "rdpmc",
830 in("ecx") index,
831 out("eax") lo,
832 out("edx") hi
833 );
834
835 (lo as u64) | ((hi as u64) << u32::BITS)
836 }
837}
838
839#[cfg(test)]
840mod tests {
841 use super::*;
842
843 #[test]
844 fn buf_copy_over_split() {
845 let mut out = [0; 7];
846 let mut buf = ByteBuffer::Split([b"aaaaaa", b"bbbbb"]);
847 buf.copy_to_slice(&mut out);
848 assert_eq!(&out, b"aaaaaab");
849 assert_eq!(buf.len(), 4);
850 }
851
852 #[test]
853 fn buf_copy_to_split() {
854 let mut out = [0; 6];
855 let mut buf = ByteBuffer::Split([b"aaaaaa", b"bbbbb"]);
856 buf.copy_to_slice(&mut out);
857
858 assert_eq!(&out, b"aaaaaa");
859 assert_eq!(buf.len(), 5);
860 }
861
862 #[test]
863 fn buf_copy_before_split() {
864 let mut out = [0; 5];
865 let mut buf = ByteBuffer::Split([b"aaaaaa", b"bbbbb"]);
866 buf.copy_to_slice(&mut out);
867
868 assert_eq!(&out, b"aaaaa");
869 assert_eq!(buf.len(), 6);
870 }
871
872 #[test]
873 fn buf_truncate_over_split() {
874 let mut out = [0u8; 11];
875 let mut buf = ByteBuffer::Split([b"1234567890", b"abc"]);
876
877 buf.truncate(11);
878 assert_eq!(buf.len(), 11);
879
880 buf.copy_to_slice(&mut out);
881 assert_eq!(&out, b"1234567890a");
882 }
883
884 #[test]
885 fn buf_truncate_before_split() {
886 let mut out = [0u8; 5];
887 let mut buf = ByteBuffer::Split([b"1234567890", b"abc"]);
888
889 buf.truncate(5);
890 assert_eq!(buf.len(), 5);
891
892 buf.copy_to_slice(&mut out);
893 assert_eq!(&out, b"12345");
894 }
895}