1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
mod record;
pub use record::*;

use std::collections::HashMap;
use std::fs::File;
use std::io::{Error, ErrorKind, Read, Result};

const TRC_TRACE_CPU_CHANGE: u32 = 0x0001f003;
const TRC_SCHED_TO_RUN: u32 = 0x00021f0f;

#[derive(Debug)]
pub struct Parser {
    // Host CPUs fiels
    cpu_current: u8,
    cpu_domains: HashMap<u8, Domain>,
    // Records fiels
    tsc_last: u64,
    records: Vec<Record>,
}

impl Parser {
    // PUBLIC FNs
    pub fn new(path: &str) -> Result<Self> {
        let mut instance = Self {
            // Host CPUs fiels
            cpu_current: 0,
            cpu_domains: HashMap::new(),
            // Records fiels
            tsc_last: 0,
            records: Vec::new(),
        };

        let _ = instance.read_file(path)?;
        Ok(instance)
    }

    pub fn get_records(&self) -> &Vec<Record> {
        &self.records
    }

    pub fn cpu_count(&self) -> u8 {
        let cpu_max = self.cpu_domains.keys().max().unwrap();
        cpu_max + 1
    }

    // PRIVATE FNs
    fn read_file(&mut self, path: &str) -> Result<()> {
        {
            let mut file = File::open(path)?;

            loop {
                let record = self.read_record(&mut file);
                match record {
                    Ok(r) => self.records.push(r),
                    Err(e) => match e.kind() {
                        ErrorKind::Other => continue,
                        _ => break,
                    },
                }
            }
        } // File closed

        self.records.sort_unstable();
        Ok(())
    }

    fn read_u32(file: &mut File) -> Result<u32> {
        let mut buf = [0u8; 4];
        file.read_exact(&mut buf)?;
        Ok(u32::from_ne_bytes(buf)) // host-endian because of XenTrace
    }

    fn read_tsc(hdr: u32, file: &mut File) -> Result<Option<u64>> {
        let in_tsc = (hdr & (1 << 31)) != 0;
        match in_tsc {
            false => Ok(None),
            true => {
                let mut buf = [0u8; 8];
                file.read_exact(&mut buf)?;
                Ok(Some(u64::from_ne_bytes(buf))) // host-endian because of XenTrace
            }
        }
    }

    fn read_extra(hdr: u32, file: &mut File) -> Result<Vec<u32>> {
        let n_extra = (hdr >> 28) & 7;
        let mut extra: Vec<u32> = Vec::new();

        for _ in 0..n_extra {
            let value = Self::read_u32(file)?;
            extra.push(value);
        }

        Ok(extra)
    }

    fn read_record(&mut self, file: &mut File) -> Result<Record> {
        // Read header
        let hdr = Self::read_u32(file)?;

        // Read event data
        let code = hdr & 0x0fffffff;
        let tsc = Self::read_tsc(hdr, file)?;
        let extra = Self::read_extra(hdr, file)?;

        // Handle special events
        if code == TRC_TRACE_CPU_CHANGE {
            let cpu = *extra.get(0).unwrap() as u8;
            self.cpu_current = cpu;
            return Err(Error::from(ErrorKind::Other)); // Do not save
        } else {
            let is_sched_min = code == (code & TRC_SCHED_TO_RUN);
            if is_sched_min {
                let dom_u32 = *extra.get(0).unwrap();
                let dom = Domain::from_u32(dom_u32);
                self.cpu_domains.insert(self.cpu_current, dom);
            }
        }

        // Create event
        let mut event = Event::new(code);
        event.set_extra(extra.as_slice());
        match tsc {
            None => event.set_tsc(self.tsc_last),
            Some(v) => {
                self.tsc_last = v;
                event.set_tsc(v);
            }
        }

        // Get current domain
        let domain = self.cpu_domains.get(&self.cpu_current);
        let domain = match domain {
            None => Domain::default(),
            Some(&v) => v,
        };

        // Create record
        let record = Record::new(self.cpu_current, domain, event);
        Ok(record)
    }
}