use std::array::TryFromSliceError;
pub const PERF_RECORD_ALIGNMENT: usize = 8;
pub fn align_to_perf_record(size: usize) -> usize {
(size + (PERF_RECORD_ALIGNMENT - 1)) & !(PERF_RECORD_ALIGNMENT - 1)
}
pub const PERF_SAMPLE_IP: u64 = 1 << 0;
pub const PERF_SAMPLE_TID: u64 = 1 << 1;
pub const PERF_SAMPLE_TIME: u64 = 1 << 2;
pub const PERF_SAMPLE_ADDR: u64 = 1 << 3;
pub const PERF_SAMPLE_READ: u64 = 1 << 4;
pub const PERF_SAMPLE_CALLCHAIN: u64 = 1 << 5;
pub const PERF_SAMPLE_ID: u64 = 1 << 6;
pub const PERF_SAMPLE_CPU: u64 = 1 << 7;
pub const PERF_SAMPLE_PERIOD: u64 = 1 << 8;
pub const PERF_SAMPLE_STREAM_ID: u64 = 1 << 9;
pub const PERF_SAMPLE_RAW: u64 = 1 << 10;
pub const PERF_SAMPLE_BRANCH_STACK: u64 = 1 << 11;
pub const PERF_SAMPLE_REGS_USER: u64 = 1 << 12;
pub const PERF_SAMPLE_STACK_USER: u64 = 1 << 13;
pub const PERF_SAMPLE_WEIGHT: u64 = 1 << 14;
pub const PERF_SAMPLE_DATA_SRC: u64 = 1 << 15;
pub const PERF_SAMPLE_IDENTIFIER: u64 = 1 << 16;
pub const PERF_SAMPLE_TRANSACTION: u64 = 1 << 17;
pub const PERF_SAMPLE_REGS_INTR: u64 = 1 << 18;
pub const PERF_SAMPLE_PHYS_ADDR: u64 = 1 << 19;
pub const PERF_SAMPLE_AUX: u64 = 1 << 20;
pub const PERF_SAMPLE_CGROUP: u64 = 1 << 21;
pub const PERF_SAMPLE_REGS_ABI_NONE: u64 = 0;
pub const PERF_SAMPLE_REGS_ABI_32: u64 = 1;
pub const PERF_SAMPLE_REGS_ABI_64: u64 = 2;
pub const PERF_RECORD_LOST: u32 = 2;
pub const PERF_RECORD_COMM: u32 = 3;
pub const PERF_RECORD_EXIT: u32 = 4;
pub const PERF_RECORD_FORK: u32 = 7;
pub const PERF_RECORD_SAMPLE: u32 = 9;
pub const PERF_RECORD_MMAP2: u32 = 10;
pub const PERF_RECORD_LOST_SAMPLES: u32 = 13;
pub const PERF_RECORD_SWITCH_CPU_WIDE: u32 = 15;
pub const PERF_FORMAT_TOTAL_TIME_ENABLED: u64 = 1 << 0;
pub const PERF_FORMAT_TOTAL_TIME_RUNNING: u64 = 1 << 1;
pub const PERF_FORMAT_ID: u64 = 1 << 2;
pub const PERF_FORMAT_GROUP: u64 = 1 << 3;
pub const PERF_FORMAT_LOST: u64 = 1 << 4;
pub const FLAG_DISABLED: u64 = 1 << 0;
pub const FLAG_INHERIT: u64 = 1 << 1;
pub const FLAG_PINNED: u64 = 1 << 2;
pub const FLAG_EXCLUSIVE: u64 = 1 << 3;
pub const FLAG_EXCLUDE_USER: u64 = 1 << 4;
pub const FLAG_EXCLUDE_KERNEL: u64 = 1 << 5;
pub const FLAG_EXCLUDE_HV: u64 = 1 << 6;
pub const FLAG_EXCLUDE_IDLE: u64 = 1 << 7;
pub const FLAG_MMAP: u64 = 1 << 8;
pub const FLAG_COMM: u64 = 1 << 9;
pub const FLAG_FREQ: u64 = 1 << 10;
pub const FLAG_INHERIT_STAT: u64 = 1 << 11;
pub const FLAG_ENABLE_ON_EXEC: u64 = 1 << 12;
pub const FLAG_TASK: u64 = 1 << 13;
pub const FLAG_WATERMARK: u64 = 1 << 14;
pub const FLAG_PRECISE_IP: u64 = 1 << 15;
pub const FLAG_PRECISE_NO_SKID: u64 = 1 << 16;
pub const FLAG_MMAP_DATA: u64 = 1 << 17;
pub const FLAG_SAMPLE_ID_ALL: u64 = 1 << 18;
pub const FLAG_EXCLUDE_HOST: u64 = 1 << 19;
pub const FLAG_EXCLUDE_GUEST: u64 = 1 << 20;
pub const FLAG_EXCLUDE_CALLCHAIN_KERNEL: u64 = 1 << 21;
pub const FLAG_EXCLUDE_CALLCHAIN_USER: u64 = 1 << 22;
pub const FLAG_MMAP2: u64 = 1 << 23;
pub const FLAG_COMM_EXEC: u64 = 1 << 24;
pub const FLAG_USE_CLOCKID: u64 = 1 << 25;
pub const FLAG_CONTEXT_SWITCH: u64 = 1 << 26;
pub const FLAG_WRITE_BACKWARD: u64 = 1 << 27;
pub const FLAG_NAMESPACES: u64 = 1 << 28;
pub const FLAG_KSYMBOL: u64 = 1 << 29;
pub const FLAG_BPF_EVENT: u64 = 1 << 30;
pub const FLAG_AUX_OUTPUT: u64 = 1 << 31;
pub const FLAG_CGROUP: u64 = 1 << 32;
pub const FLAG_TEXT_POKE: u64 = 1 << 33;
pub const PERF_TYPE_HARDWARE: u32 = 0;
pub const PERF_TYPE_SOFTWARE: u32 = 1;
pub const PERF_TYPE_TRACEPOINT: u32 = 2;
pub const PERF_EVENT_IOC_ENABLE: i32 = 9216;
pub const PERF_EVENT_IOC_DISABLE: i32 = 9217;
pub const PERF_EVENT_IOC_SET_OUTPUT: i32 = 9221;
pub const PERF_EVENT_IOC_SET_FILTER: i32 = 0x40082406;
pub const PERF_COUNT_SW_CPU_CLOCK: u64 = 0;
pub const PERF_COUNT_SW_PAGE_FAULTS_MIN: u64 = 2;
pub const PERF_COUNT_SW_CONTEXT_SWITCHES: u64 = 3;
pub const PERF_COUNT_SW_PAGE_FAULTS_MAJ: u64 = 6;
pub const PERF_COUNT_SW_BPF_OUTPUT: u64 = 10;
pub const PERF_COUNT_SW_DUMMY: u64 = 9;
pub const PERF_ATTR_SIZE_VER4: u32 = 104;
#[cfg(target_arch = "x86_64")]
pub const PERF_REG_BP: u64 = 1 << 6u64;
#[cfg(target_arch = "x86_64")]
pub const PERF_REG_SP: u64 = 1 << 7u64;
#[cfg(target_arch = "x86_64")]
pub const PERF_REG_IP: u64 = 1 << 8u64;
#[cfg(target_arch = "aarch64")]
pub const PERF_REG_BP: u64 = 1 << 30u64;
#[cfg(target_arch = "aarch64")]
pub const PERF_REG_SP: u64 = 1 << 31u64;
#[cfg(target_arch = "aarch64")]
pub const PERF_REG_IP: u64 = 1 << 32u64;
pub const PERF_CONTEXT_HV: u64 = 0xFFFFFFFFFFFFFFE0;
pub const PERF_CONTEXT_KERNEL: u64 = 0xFFFFFFFFFFFFFF80;
pub const PERF_CONTEXT_USER: u64 = 0xFFFFFFFFFFFFFE00;
pub const PERF_CONTEXT_GUEST: u64 = 0xFFFFFFFFFFFFF800;
pub const PERF_CONTEXT_GUEST_KERNEL: u64 = 0xFFFFFFFFFFFFF780;
pub const PERF_CONTEXT_GUEST_USER: u64 = 0xFFFFFFFFFFFFF600;
pub const PERF_CONTEXT_MAX: u64 = 0xFFFFFFFFFFFFF001;
pub const PERF_RECORD_MISC_MMAP_DATA: u16 = 1 << 13u16;
pub const PERF_RECORD_MISC_COMM_EXEC: u16 = 1 << 13u16;
pub const PERF_RECORD_MISC_FORK_EXEC: u16 = 1 << 13u16;
pub const PERF_RECORD_MISC_SWITCH_OUT: u16 = 1 << 13u16;
pub const PERF_RECORD_MISC_EXACT_IP: u16 = 1 << 14u16;
pub const PERF_RECORD_MISC_SWITCH_OUT_PREEMPT: u16 = 1 << 14u16;
pub const PERF_RECORD_MISC_MMAP_BUILD_ID: u16 = 1 << 14u16;
#[repr(C)]
#[derive(Copy)]
#[derive(Clone)]
#[derive(Default)]
pub struct perf_event_attr {
pub event_type: u32,
pub size: u32,
pub config: u64,
pub sample_period_freq: u64,
pub sample_type: u64,
pub read_format: u64,
pub flags: u64,
pub wakeup_events_watermark: u32,
pub bp_type: u32,
pub bp_addr: u64,
pub bp_len: u64,
pub branch_sample_type: u64,
pub sample_regs_user: u64,
pub sample_stack_user: u32,
pub clockid: i32,
pub sample_regs_intr: u64,
}
#[derive(Default)]
pub struct SampleIdOffsets {
pub pid: Option<usize>,
pub tid: Option<usize>,
pub time: Option<usize>,
pub id: Option<usize>,
pub stream_id: Option<usize>,
pub cpu: Option<usize>,
pub identifier: Option<usize>,
pub size: usize,
}
impl perf_event_attr {
pub fn has_flag(
&self,
flag: u64) -> bool {
(self.flags & flag) == flag
}
pub fn has_format(
&self,
format: u64) -> bool {
(self.sample_type & format) == format
}
pub fn has_read_format(
&self,
format: u64) -> bool {
(self.read_format & format) == format
}
pub fn non_sampled_id_offsets(&self) -> Option<SampleIdOffsets> {
if !self.has_flag(FLAG_SAMPLE_ID_ALL) {
return None;
}
let mut offset = SampleIdOffsets::default();
if self.has_format(PERF_SAMPLE_TID) {
offset.pid = Some(offset.size);
offset.size += 4;
offset.tid = Some(offset.size);
offset.size += 4;
}
if self.has_format(PERF_SAMPLE_TIME) {
offset.time = Some(offset.size);
offset.size += 8;
}
if self.has_format(PERF_SAMPLE_ID) {
offset.id = Some(offset.size);
offset.size += 8;
}
if self.has_format(PERF_SAMPLE_STREAM_ID) {
offset.stream_id = Some(offset.size);
offset.size += 8;
}
if self.has_format(PERF_SAMPLE_CPU) {
offset.cpu = Some(offset.size);
offset.size += 8;
}
if self.has_format(PERF_SAMPLE_IDENTIFIER) {
offset.identifier = Some(offset.size);
offset.size += 8;
}
Some(offset)
}
}
pub struct Header<'a> {
pub entry_type: u32,
pub misc: u16,
pub size: u16,
pub data: &'a [u8],
}
impl<'a> Header<'a> {
pub fn from_slice(slice: &'a [u8]) -> Result<Header<'a>, TryFromSliceError> {
Ok(Self {
entry_type: Self::entry_type(slice)?,
misc: Self::misc(slice)?,
size: Self::size(slice)?,
data: Self::data(slice),
})
}
fn entry_type(slice: &[u8]) -> Result<u32, TryFromSliceError> {
let slice = slice[0..4].try_into()?;
Ok(u32::from_ne_bytes(slice))
}
fn misc(slice: &[u8]) -> Result<u16, TryFromSliceError> {
let slice = slice[4..6].try_into()?;
Ok(u16::from_ne_bytes(slice))
}
fn size(slice: &[u8]) -> Result<u16, TryFromSliceError> {
let slice = slice[6..8].try_into()?;
Ok(u16::from_ne_bytes(slice))
}
pub fn data_offset() -> usize {
8
}
fn data(slice: &[u8]) -> &[u8] {
&slice[Self::data_offset()..]
}
pub fn write(
entry_type: u32,
misc: u16,
data: &[u8],
output: &mut Vec<u8>) {
let unaligned_size = data.len() + 8;
let size = align_to_perf_record(unaligned_size) as u16;
output.extend_from_slice(&entry_type.to_ne_bytes());
output.extend_from_slice(&misc.to_ne_bytes());
output.extend_from_slice(&size.to_ne_bytes());
output.extend_from_slice(data);
let padding = (size as usize) - unaligned_size;
if padding > 0 {
output.resize(output.len() + padding, 0);
}
}
}
pub struct Sample {
}
impl Sample {
pub fn write_time(
time: u64,
output: &mut Vec<u8>) {
output.extend_from_slice(&time.to_ne_bytes());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_rw() {
let mut data = Vec::new();
let magic: u32 = 1234;
let magic_slice = magic.to_ne_bytes();
Header::write(1024, 0, &magic_slice, &mut data);
let slice = data.as_slice();
let header = Header::from_slice(slice).unwrap();
assert_eq!(1024, header.entry_type);
assert_eq!(0, header.misc);
assert_eq!(16, header.size);
let data_slice = header.data;
let magic_slice = data_slice[0..4].try_into().unwrap();
assert_eq!(1234, u32::from_ne_bytes(magic_slice));
assert_eq!([0u8; 4], data_slice[4..8]);
}
#[test]
fn sample_raw_aligned() {
let mut data = Vec::new();
let raw = [1u8, 2u8, 3u8];
let len = raw.len() as u32;
let field_size = 4 + raw.len();
let aligned_field_size = align_to_perf_record(field_size);
let padding = aligned_field_size - field_size;
data.extend_from_slice(&len.to_ne_bytes());
data.extend_from_slice(&raw);
if padding > 0 {
data.resize(data.len() + padding, 0);
}
assert_eq!(8, data.len());
assert_eq!(3u32.to_ne_bytes(), data[0..4]);
assert_eq!([1u8, 2u8, 3u8], data[4..7]);
assert_eq!(0u8, data[7]);
}
}