use std::fs::File;
use std::io::{Error, Result};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use arena::Arena;
use auxiliary::AuxTracer;
use iter::{CowIter, Iter};
use rb::Rb;
use record::{Parser, UnsafeParser};
use crate::ffi::{bindings as b, syscall, Attr, Metadata, PAGE_SIZE};
mod arena;
pub mod auxiliary;
pub mod iter;
pub mod rb;
pub mod record;
pub struct Sampler {
perf: Arc<File>,
arena: Arena,
parser: Parser,
}
impl Sampler {
pub(super) fn new(perf: Arc<File>, attr: &Attr, exp: u8) -> Result<Self> {
let Some(len) = 2_usize
.checked_pow(exp as u32)
.and_then(|n| n.checked_add(1))
.and_then(|n| n.checked_mul(*PAGE_SIZE))
else {
return Err(Error::other("allocation size overflow"));
};
let arena = Arena::new(&perf, len, 0)?;
Ok(Sampler {
perf,
arena,
parser: Parser(UnsafeParser::from_attr(attr)),
})
}
pub fn iter(&self) -> Iter<'_> {
let alloc = self.arena.as_slice();
let metadata = unsafe { &mut *(alloc.as_ptr() as *mut Metadata) };
let rb = Rb::new(
&alloc[*PAGE_SIZE..],
unsafe { AtomicU64::from_ptr(&mut metadata.data_tail as _) },
unsafe { AtomicU64::from_ptr(&mut metadata.data_head as _) },
);
Iter(CowIter {
rb,
perf: &self.perf,
parser: &self.parser,
})
}
pub fn parser(&self) -> &UnsafeParser {
&self.parser.0
}
pub fn aux_tracer(&self, exp: u8) -> Result<AuxTracer<'_>> {
let alloc = self.arena.as_slice();
let metadata = unsafe { &mut *(alloc.as_ptr() as *mut Metadata) };
AuxTracer::new(&self.perf, metadata, exp)
}
pub fn pause(&self) -> Result<()> {
#[cfg(feature = "linux-4.7")]
return {
syscall!(ioctl_arg, &self.perf, b::PERF_IOC_OP_PAUSE_OUTPUT as u64, 1)?;
Ok(())
};
#[cfg(not(feature = "linux-4.7"))]
return Err(std::io::ErrorKind::Unsupported.into());
}
pub fn resume(&self) -> Result<()> {
#[cfg(feature = "linux-4.7")]
return {
syscall!(ioctl_arg, &self.perf, b::PERF_IOC_OP_PAUSE_OUTPUT as u64, 0)?;
Ok(())
};
#[cfg(not(feature = "linux-4.7"))]
return Err(std::io::ErrorKind::Unsupported.into());
}
pub fn enable_counter_with(&self, max_samples: u32) -> Result<()> {
syscall!(
ioctl_arg,
&self.perf,
b::PERF_IOC_OP_REFRESH as u64,
max_samples as u64
)?;
Ok(())
}
pub fn sample_on(&self, freq_or_count: u64) -> Result<()> {
syscall!(
ioctl_arg,
&self.perf,
b::PERF_IOC_OP_PERIOD as u64,
freq_or_count,
)?;
Ok(())
}
fn metadata_inner(&self) -> *mut Metadata {
let alloc_ptr = self.arena.as_slice().as_ptr();
alloc_ptr as *mut Metadata
}
pub fn counter_time_enabled(&self) -> u64 {
let metadata = self.metadata_inner();
let metadata = unsafe { &mut *metadata };
let time_enabled = unsafe { AtomicU64::from_ptr(&mut metadata.time_enabled as _) };
time_enabled.load(Ordering::Relaxed)
}
pub fn counter_time_running(&self) -> u64 {
let metadata = self.metadata_inner();
let metadata = unsafe { &mut *metadata };
let time_running = unsafe { AtomicU64::from_ptr(&mut metadata.time_running as _) };
time_running.load(Ordering::Relaxed)
}
}
unsafe impl Send for Sampler {}