linux-perf-file-reader 0.2.0

Library for parse perf.data file from Linux perf tool
Documentation
use crate::*;
use header::*;
use tools::{collect_n, read_raw, read_string};

pub fn read_info(file: &mut File, header: &PerfHeader) -> io::Result<Info> {
    let mut reader = HeaderInfoReader::new(file, header)?;

    reader.skip(HeaderFlags::TRACING_DATA);
    reader.skip(HeaderFlags::BUILD_ID);
    let hostname = reader.get_string(HeaderFlags::HOSTNAME)?;
    let os_release = reader.get_string(HeaderFlags::OSRELEASE)?;
    let tools_version = reader.get_string(HeaderFlags::VERSION)?;
    let arch = reader.get_string(HeaderFlags::ARCH)?;
    let cpu_count = reader.get::<CpuCount>(HeaderFlags::NRCPUS)?;
    let cpu_description = reader.get_string(HeaderFlags::CPUDESC)?;
    let cpu_id = reader.get_string(HeaderFlags::CPUID)?;
    let total_memory = reader.get::<u64>(HeaderFlags::TOTAL_MEM)?;
    let command_line = reader.get_string_array(HeaderFlags::CMDLINE)?;
    let event_descriptions = reader.get_event_description()?;
    let cpu_topology = reader.get_string_array(HeaderFlags::CPU_TOPOLOGY)?;
    reader.skip(HeaderFlags::NUMA_TOPOLOGY);
    reader.skip(HeaderFlags::BRANCH_STACK);
    reader.skip(HeaderFlags::PMU_MAPPINGS);
    reader.skip(HeaderFlags::GROUP_DESC);
    reader.skip(HeaderFlags::AUXTRACE);
    reader.skip(HeaderFlags::STAT);
    reader.skip(HeaderFlags::CACHE);
    if reader.has_more() {
        warn!("Unknown flags in header");
    }

    Ok(Info {
        hostname,
        os_release,
        tools_version,
        cpu_count,
        event_descriptions,
        arch,
        cpu_id,
        cpu_description,
        total_memory,
        command_line,
        cpu_topology,
    })
}

struct HeaderInfoReader<'a> {
    file: &'a mut File,
    sections: Vec<PerfFileSection>,
    current: usize,
    flags: HeaderFlags,
}

fn bits_count(mut v: u64) -> u8 {
    let mut c = 0;
    while v != 0 {
        v &= v - 1;
        c += 1;
    }
    c
}

impl<'a> HeaderInfoReader<'a> {
    fn new(file: &'a mut File, header: &PerfHeader) -> io::Result<Self> {
        file.seek(io::SeekFrom::Start(header.data.offset + header.data.size))?;
        let sections: Vec<PerfFileSection> =
            collect_n(bits_count(header.flags.bits()) as usize, || read_raw(file))?;
        debug!(
            "flags: {:x}, have {} info records, start at 0x{:x}",
            header.flags.bits(),
            sections.len(),
            header.data.offset + header.data.size
        );
        Ok(HeaderInfoReader {
            file: file,
            sections: sections,
            current: 0,
            flags: header.flags,
        })
    }

    fn seek(&mut self, flag: HeaderFlags) -> io::Result<()> {
        let section = &self.sections[self.current];
        debug!(
            "Read section {} {:?}, size {}",
            self.current, flag, section.size
        );
        self.file.seek(io::SeekFrom::Start(section.offset))?;
        self.current += 1;
        Ok(())
    }

    fn get_string(&mut self, flag: HeaderFlags) -> io::Result<Option<String>> {
        if self.flags.contains(flag) && self.sections.len() > self.current {
            self.seek(flag)?;
            let size: u32 = read_raw(self.file)?;
            Ok(Some(read_string(self.file, size as usize)?))
        } else {
            Ok(None)
        }
    }

    fn get_string_array(&mut self, flag: HeaderFlags) -> io::Result<Option<Vec<String>>> {
        if self.flags.contains(flag) && self.sections.len() > self.current {
            self.seek(flag)?;
            let count: u32 = read_raw(self.file)?;
            Ok(Some(collect_n(count as usize, || {
                let size: u32 = read_raw(self.file)?;
                read_string(self.file, size as usize)
            })?))
        } else {
            Ok(None)
        }
    }

    fn get_event_description(&mut self) -> io::Result<Option<Vec<EventDescription>>> {
        if self.flags.contains(HeaderFlags::EVENT_DESC) && self.sections.len() > self.current {
            self.seek(HeaderFlags::EVENT_DESC)?;
            let count: u32 = read_raw(self.file)?;
            let size: u32 = read_raw(self.file)?;
            debug!(" EVENT_DESC: count {}, size {}", count, size);
            let all_attributes = collect_n(count as usize, || {
                let attributes: EventAttributes = read_raw(self.file)?;
                self.file.seek(io::SeekFrom::Current(
                    size as i64 - mem::size_of::<EventAttributes>() as i64,
                ))?;
                let id_count: u32 = read_raw(self.file)?;
                let name_size: u32 = read_raw(self.file)?;
                let name = read_string(self.file, name_size as usize)?;
                let ids: io::Result<Vec<u64>> =
                    collect_n(id_count as usize, || read_raw(self.file)?);
                ids.map(|ids| EventDescription {
                    attributes,
                    name,
                    ids,
                })
            })?;
            Ok(Some(all_attributes))
        } else {
            Ok(None)
        }
    }

    fn get<T>(&mut self, flag: HeaderFlags) -> io::Result<Option<T>> {
        if self.flags.contains(flag) && self.sections.len() > self.current {
            self.seek(flag)?;
            let v: T = read_raw(self.file)?;
            Ok(Some(v))
        } else {
            Ok(None)
        }
    }

    fn skip(&mut self, flag: HeaderFlags) {
        if self.flags.contains(flag) && self.sections.len() > self.current {
            debug!(
                "Skip section {} {:?} size {}",
                self.current, flag, self.sections[self.current].size
            );
            self.current += 1;
        }
    }

    fn has_more(&self) -> bool {
        self.sections.len() > self.current
    }
}