perf 0.0.2

Linux perf API access
Documentation
// Copyright (C) 2017 - Will Glozer. All rights reserved.

use std::os::raw::c_int;
use errno::errno;
use libc::*;
use crate::Error;
use crate::ffi::perf_event_attr;

IOCTL!(PERF_EVENT_IOC_ENABLE,  b'$', 0);
IOCTL!(PERF_EVENT_IOC_DISABLE, b'$', 1);
IOCTL!(PERF_EVENT_IOC_RESET,   b'$', 3);
IOCTL!(PERF_EVENT_IOC_SET_BPF, b'$', 8, c_int);

pub fn perf_event_open(attr: *const perf_event_attr, pid: pid_t, cpu: c_int, group_fd: c_int, flags: u64) -> Result<c_int, Error> {
    unsafe {
        match syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags) {
            -1 => Err(errno())?,
            rc => Ok(rc as c_int),
        }
    }
}

pub fn perf_event_ioc_enable(fd: c_int) -> Result<(), Error> {
    unsafe {
        match ioctl(fd, PERF_EVENT_IOC_ENABLE) {
            0 => Ok(()),
            _ => Err(errno())?
        }
    }
}

pub fn perf_event_ioc_disable(fd: c_int) -> Result<(), Error> {
    unsafe {
        match ioctl(fd, PERF_EVENT_IOC_DISABLE) {
            0 => Ok(()),
            _ => Err(errno())?
        }
    }
}

pub fn perf_event_ioc_reset(fd: c_int) -> Result<(), Error> {
    unsafe {
        match ioctl(fd, PERF_EVENT_IOC_RESET) {
            0 => Ok(()),
            _ => Err(errno())?
        }
    }
}

pub fn perf_event_ioc_set_bpf(fd: c_int, pfd: c_int) -> Result<(), Error> {
    unsafe {
        match ioctl(fd, PERF_EVENT_IOC_SET_BPF, pfd) {
            0 => Ok(()),
            _ => Err(errno())?
        }
    }
}

#[cfg(test)]
mod tests {
    use std::error::Error;
    use std::fs::File;
    use std::io::Read;
    use std::mem;
    use crate::ffi::*;
    use super::*;

    #[test]
    fn test_perf_events() {
        check_paranoia(2).unwrap();

        let mut attr = perf_event_attr::default();
        attr.type_       = PERF_TYPE_SOFTWARE;
        attr._bitfield_1 = 1 | 1 << 5 | 1 << 6;
        attr.config      = PERF_COUNT_SW_TASK_CLOCK;

        let fd = perf_event_open(&attr, 0, -1, -1, 0).unwrap();
        perf_event_ioc_reset(fd).unwrap();
        perf_event_ioc_enable(fd).unwrap();

        println!("hello, world!");

        perf_event_ioc_disable(fd).unwrap();

        let mut count: c_ulong = 0;
        unsafe {
            let data = &mut count as *mut _ as *mut c_void;
            let size = mem::size_of_val(&count);
            read(fd, data, size);
        }
        assert!(count > 0);
    }

    fn check_paranoia(level: u8) -> Result<(), Box<dyn Error>> {
        let mut file = File::open("/proc/sys/kernel/perf_event_paranoid")?;
        let mut line = String::new();
        file.read_to_string(&mut line)?;

        if  line.trim().parse::<u8>()? > level {
            panic!(r"
                test requires paranoia level <= {0} or CAP_SYS_ADMIN

                echo {0} > /proc/sys/kernel/perf_event_paranoid
            ", level);
        }

        Ok(())
    }
}