use std::borrow::Borrow;
use std::cell::UnsafeCell;
use std::io::{self, Result};
use std::mem;
use std::os::fd::AsRawFd;
use std::rc::Rc;
use std::sync::Arc;
use super::{Counter, Stat};
use crate::config::sibling::attr::from;
use crate::config::sibling::Opts;
use crate::event::Event;
use crate::ffi::{bindings as b, syscall};
pub struct CounterGroup {
leader: Counter,
siblings: Vec<Rc<Counter>>,
}
impl CounterGroup {
pub fn from(leader: Counter) -> Self {
Self {
leader,
siblings: vec![],
}
}
pub fn leader(&self) -> &Counter {
&self.leader
}
pub fn siblings(&self) -> &[Rc<Counter>] {
self.siblings.as_slice()
}
pub fn add(
&mut self,
event: impl TryInto<Event, Error = io::Error>,
opts: impl Borrow<Opts>,
) -> Result<Rc<Counter>> {
let leader = &self.leader;
let attr = {
let leader_attr = unsafe { &*leader.attr.get() };
from(event.try_into()?.0, opts.borrow(), leader_attr)?
};
let group_fd = leader.perf.as_raw_fd();
let flags = leader.target.flags | b::PERF_FLAG_FD_CLOEXEC as u64;
let perf = syscall!(
perf_event_open,
&attr,
leader.target.pid,
leader.target.cpu,
group_fd,
flags
)?;
let read_buf = vec![0; Stat::read_buf_size(1, attr.read_format)];
let sibling = Rc::new(Counter {
target: leader.target.clone(),
attr: UnsafeCell::new(attr),
perf: Arc::new(perf),
read_buf: UnsafeCell::new(read_buf),
});
self.siblings.push(Rc::clone(&sibling));
let leader_read_format = unsafe { &*leader.attr.get() }.read_format;
let new_len = Stat::read_buf_size(self.siblings.len() + 1, leader_read_format);
let old = unsafe { &mut *leader.read_buf.get() };
if new_len > old.len() {
let new = vec![0; new_len];
let _ = mem::replace(old, new);
}
Ok(sibling)
}
pub fn enable(&self) -> Result<()> {
syscall!(
unsafe,
ioctl_arg,
&self.leader.perf,
b::PERF_IOC_OP_ENABLE as u64,
b::PERF_IOC_FLAG_GROUP as u64,
)?;
Ok(())
}
pub fn disable(&self) -> Result<()> {
syscall!(
unsafe,
ioctl_arg,
&self.leader.perf,
b::PERF_IOC_OP_DISABLE as u64,
b::PERF_IOC_FLAG_GROUP as u64,
)?;
Ok(())
}
pub fn clear_count(&self) -> Result<()> {
syscall!(
unsafe,
ioctl_arg,
&self.leader.perf,
b::PERF_IOC_OP_RESET as u64,
b::PERF_IOC_FLAG_GROUP as u64,
)?;
Ok(())
}
}