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}