use std::io::{Read, Seek, SeekFrom};
use byteorder::{ByteOrder, ReadBytesExt};
use linear_map::LinearMap;
use linux_perf_event_reader::PerfEventAttr;
use super::section::PerfFileSection;
use crate::{Error, ReadError};
#[derive(Debug, Clone, Copy)]
pub struct NrCpus {
pub nr_cpus_available: u32,
pub nr_cpus_online: u32,
}
impl NrCpus {
pub const STRUCT_SIZE: usize = 4 + 4;
pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, std::io::Error> {
let nr_cpus_available = reader.read_u32::<T>()?;
let nr_cpus_online = reader.read_u32::<T>()?;
Ok(Self {
nr_cpus_available,
nr_cpus_online,
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct SampleTimeRange {
pub first_sample_time: u64,
pub last_sample_time: u64,
}
impl SampleTimeRange {
pub const STRUCT_SIZE: usize = 8 + 8;
pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, std::io::Error> {
let first_sample_time = reader.read_u64::<T>()?;
let last_sample_time = reader.read_u64::<T>()?;
Ok(Self {
first_sample_time,
last_sample_time,
})
}
}
pub struct HeaderString;
impl HeaderString {
pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Option<String>, std::io::Error> {
let len = reader.read_u32::<T>()?;
let mut s = vec![0; len as usize];
reader.read_exact(&mut s)?;
let actual_len = memchr::memchr(0, &s).unwrap_or(s.len());
s.truncate(actual_len);
Ok(String::from_utf8(s).ok())
}
}
#[derive(Debug, Clone)]
pub struct AttributeDescription {
pub attr: PerfEventAttr,
pub name: Option<String>,
pub event_ids: Vec<u64>,
}
impl AttributeDescription {
pub fn parse_event_desc_section<R: Read, T: ByteOrder>(
mut reader: R,
) -> Result<Vec<Self>, std::io::Error> {
let nr = reader.read_u32::<T>()?;
let mut attributes = Vec::with_capacity(nr as usize);
let attr_size = reader.read_u32::<T>()?;
for _ in 0..nr {
let attr = PerfEventAttr::parse::<_, T>(&mut reader, Some(attr_size))?;
let nr_ids = reader.read_u32::<T>()?;
let event_string = HeaderString::parse::<_, T>(&mut reader)?;
let mut ids = Vec::with_capacity(nr_ids as usize);
for _ in 0..nr_ids {
ids.push(reader.read_u64::<T>()?);
}
attributes.push(AttributeDescription {
attr,
name: event_string,
event_ids: ids,
});
}
Ok(attributes)
}
pub fn parse_event_types_section<C: Read + Seek, T: ByteOrder>(
mut cursor: C,
event_types_section: &PerfFileSection,
attr_size: u64,
) -> Result<Vec<Self>, Error> {
cursor.seek(SeekFrom::Start(event_types_section.offset))?;
let entry_size = attr_size + PerfFileSection::STRUCT_SIZE;
let entry_count = event_types_section.size / entry_size;
let mut perf_event_event_type_info = Vec::with_capacity(entry_count as usize);
for _ in 0..entry_count {
let attr = PerfEventAttr::parse::<_, T>(&mut cursor, Some(attr_size as u32))
.map_err(|_| ReadError::PerfEventAttr)?;
let event_ids = PerfFileSection::parse::<_, T>(&mut cursor)?;
perf_event_event_type_info.push((attr, event_ids));
}
let mut attributes = Vec::new();
for (attr, section) in perf_event_event_type_info {
cursor.seek(SeekFrom::Start(section.offset))?;
let id_count = section.size / 8;
let mut event_ids = Vec::with_capacity(id_count as usize);
for _ in 0..id_count {
event_ids.push(cursor.read_u64::<T>()?);
}
attributes.push(AttributeDescription {
attr,
name: None,
event_ids,
});
}
Ok(attributes)
}
pub fn parse_attr_section<C: Read + Seek, T: ByteOrder>(
mut cursor: C,
attr_section: &PerfFileSection,
attr_size: u64,
) -> Result<Vec<Self>, Error> {
cursor.seek(SeekFrom::Start(attr_section.offset))?;
let attr_count = attr_section.size / attr_size;
let mut attributes = Vec::with_capacity(attr_count as usize);
for _ in 0..attr_count {
let attr = PerfEventAttr::parse::<_, T>(&mut cursor, Some(attr_size as u32))
.map_err(|_| ReadError::PerfEventAttr)?;
attributes.push(AttributeDescription {
attr,
name: None,
event_ids: vec![],
});
}
Ok(attributes)
}
pub fn attributes(&self) -> &PerfEventAttr {
&self.attr
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn ids(&self) -> &[u64] {
&self.event_ids
}
}
pub struct PmuMappings;
impl PmuMappings {
pub fn parse<R: Read, T: ByteOrder>(
mut reader: R,
) -> Result<LinearMap<u32, String>, std::io::Error> {
let nr = reader.read_u32::<T>()?;
let mut vec = Vec::with_capacity(nr as usize);
for _ in 0..nr {
let pmu_type = reader.read_u32::<T>()?;
if let Some(pmu_name) = HeaderString::parse::<_, T>(&mut reader)? {
vec.push((pmu_type, pmu_name));
}
}
vec.sort_by_key(|item| item.0);
Ok(vec.into_iter().collect())
}
}