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::simpleperf::SimplePerfEventType;
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<C: Read + Seek, T: ByteOrder>(
mut cursor: C,
) -> Result<Vec<Self>, Error> {
let nr = cursor.read_u32::<T>()?;
let mut attributes = Vec::with_capacity(nr as usize);
let attr_size = cursor.read_u32::<T>()? as u64;
for _ in 0..nr {
let attr = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
let nr_ids = cursor.read_u32::<T>()?;
let event_string = HeaderString::parse::<_, T>(&mut cursor)?;
let mut ids = Vec::with_capacity(nr_ids as usize);
for _ in 0..nr_ids {
ids.push(cursor.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>(
cursor: C,
event_types_section: &PerfFileSection,
attr_size: u64,
) -> Result<Vec<Self>, Error> {
Self::parse_sequence_of_attr_and_id_section::<C, T>(
cursor,
event_types_section,
attr_size,
None,
)
}
pub fn parse_simpleperf_attr_section<C: Read + Seek, T: ByteOrder>(
cursor: C,
attr_section: &PerfFileSection,
attr_size: u64,
event_types: &[SimplePerfEventType],
) -> Result<Vec<Self>, Error> {
if attr_size < PerfFileSection::STRUCT_SIZE {
return Err(ReadError::PerfEventAttr.into());
}
let attr_size_without_id_section = attr_size - PerfFileSection::STRUCT_SIZE;
let event_names: Vec<_> = event_types.iter().map(|t| t.name.as_str()).collect();
Self::parse_sequence_of_attr_and_id_section::<C, T>(
cursor,
attr_section,
attr_size_without_id_section,
Some(&event_names),
)
}
fn parse_sequence_of_attr_and_id_section<C: Read + Seek, T: ByteOrder>(
mut cursor: C,
section: &PerfFileSection,
attr_size: u64,
event_names: Option<&[&str]>,
) -> Result<Vec<Self>, Error> {
cursor.seek(SeekFrom::Start(section.offset))?;
let entry_size = attr_size + PerfFileSection::STRUCT_SIZE;
let entry_count = 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 = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
let event_ids = PerfFileSection::parse::<_, T>(&mut cursor)?;
perf_event_event_type_info.push((attr, event_ids));
}
let mut attributes = Vec::new();
for (event_index, (attr, section)) in perf_event_event_type_info.into_iter().enumerate() {
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>()?);
}
let name = if let Some(names) = event_names {
names.get(event_index).map(|s| s.to_string())
} else {
None
};
attributes.push(AttributeDescription {
attr,
name,
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 = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
attributes.push(AttributeDescription {
attr,
name: None,
event_ids: vec![],
});
}
Ok(attributes)
}
fn parse_single_attr<C: Read + Seek, T: ByteOrder>(
mut cursor: C,
attr_size: u64,
) -> Result<PerfEventAttr, Error> {
let (attr, size) =
PerfEventAttr::parse::<_, T>(&mut cursor).map_err(|_| ReadError::PerfEventAttr)?;
if size > attr_size {
return Err(Error::InconsistentAttributeSizes(size, attr_size));
}
if size < attr_size {
let remaining_bytes = attr_size - size;
cursor.seek(SeekFrom::Current(remaining_bytes as i64))?;
}
Ok(attr)
}
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(pub LinearMap<u32, String>);
impl PmuMappings {
pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, 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(Self(vec.into_iter().collect()))
}
}