Skip to main content

linux_perf_file_reader/
event_reader.rs

1use crate::tools::{collect_n, read_raw, read_string};
2use crate::*;
3use std::collections::HashMap;
4use std::io::{Read, Seek};
5
6#[repr(u32)]
7#[derive(Debug)]
8#[allow(dead_code)]
9enum RecordType {
10    MMap = 1,
11    Lost = 2,
12    Comm = 3,
13    Exit = 4,
14    Fork = 7,
15    Read = 8,
16    Sample = 9,
17    MMap2 = 10,
18    Aux = 11,
19    ItraceStart = 12,
20    LostSamples = 13,
21    Switch = 14,
22    SwitchCpuWide = 15,
23    Attr = 64,
24    EventType = 65, /* depreceated */
25    TracingData = 66,
26    BuildId = 67,
27    FinishedRound = 68,
28    IdIndex = 69,
29    AuxTraceInfo = 70,
30    AuxTrace = 71,
31    AuxTraceError = 72,
32}
33
34#[repr(C)]
35#[derive(Debug)]
36struct EventHeader {
37    record_type: RecordType,
38    misc: u16,
39    size: u16,
40}
41
42#[repr(C)]
43#[derive(Debug)]
44struct MMapPart {
45    pid: u32,
46    tid: u32,
47    addr: u64,
48    len: u64,
49    pgoff: u64,
50}
51
52#[repr(C)]
53#[derive(Debug)]
54struct MMap2Part {
55    pid: u32,
56    tid: u32,
57    addr: u64,
58    len: u64,
59    pgoff: u64,
60    maj: u32,
61    min: u32,
62    ino: u64,
63    ino_generation: u64,
64    prot: u32,
65    flags: u32,
66}
67
68#[repr(C)]
69#[derive(Debug)]
70struct ExitPart {
71    pid: u32,
72    ppid: u32,
73    tid: u32,
74    ptid: u32,
75    time: u64,
76}
77
78#[repr(C)]
79#[derive(Debug)]
80struct CommPart {
81    pid: u32,
82    tid: u32,
83}
84
85macro_rules! bool_to_option {
86    ($v: expr, $c: expr) => {
87        if $v {
88            Some($c)
89        } else {
90            None
91        }
92    };
93}
94
95struct SampleReader {
96    id_to_sample_format: HashMap<u64, SampleFormat>,
97    sample_id_format: SampleFormat,
98    sample_id_size: usize,
99}
100
101impl SampleReader {
102    fn new(event_desciptions: &[EventDescription]) -> Result<Self> {
103        if event_desciptions.len() == 0 {
104            Err(ErrorKind::NoEventInInfoSection.into())
105        } else if event_desciptions.len() > 1
106            && !event_desciptions.iter().all(|d| {
107                d.attributes
108                    .sample_format
109                    .contains(SampleFormat::IDENTIFIER)
110            })
111            && !event_desciptions.iter().all(|d| {
112                d.attributes.sample_format == event_desciptions[0].attributes.sample_format
113            })
114        {
115            Err(ErrorKind::NoIndentifierInEventInInfoAttributes.into())
116        } else {
117            let first = event_desciptions[0].attributes.sample_format;
118            let map = event_desciptions
119                .iter()
120                .flat_map(|d| {
121                    d.ids
122                        .iter()
123                        .map(move |id| (*id, d.attributes.sample_format))
124                })
125                .collect();
126            Ok(SampleReader {
127                id_to_sample_format: map,
128                sample_id_format: first,
129                sample_id_size: Self::sample_id_size(&first),
130            })
131        }
132    }
133
134    fn sample_id_size(s: &SampleFormat) -> usize {
135        let mut size = 0;
136        if s.contains(SampleFormat::TID) {
137            size += 8;
138        }
139        if s.contains(SampleFormat::TIME) {
140            size += 8;
141        }
142        if s.contains(SampleFormat::ID) {
143            size += 8;
144        }
145        if s.contains(SampleFormat::STREAM_ID) {
146            size += 8;
147        }
148        if s.contains(SampleFormat::CPU) {
149            size += 8;
150        }
151        if s.contains(SampleFormat::IDENTIFIER) {
152            size += 8;
153        }
154        size
155    }
156
157    fn read_sample_id(&self, file: &mut impl Read) -> io::Result<SampleId> {
158        let s = self.sample_id_format;
159        let pid = bool_to_option!(s.contains(SampleFormat::TID), read_raw(file)?);
160        let tid = bool_to_option!(s.contains(SampleFormat::TID), read_raw(file)?);
161        let time = bool_to_option!(s.contains(SampleFormat::TIME), read_raw(file)?);
162        let id = bool_to_option!(s.contains(SampleFormat::ID), read_raw(file)?);
163        let stream_id = bool_to_option!(s.contains(SampleFormat::STREAM_ID), read_raw(file)?);
164        let cpu = bool_to_option!(s.contains(SampleFormat::CPU), read_raw(file)?);
165        let res = bool_to_option!(s.contains(SampleFormat::CPU), read_raw(file)?);
166        let identifier = bool_to_option!(s.contains(SampleFormat::IDENTIFIER), read_raw(file)?);
167        Ok(SampleId {
168            pid,
169            tid,
170            time,
171            id,
172            stream_id,
173            cpu,
174            res,
175            identifier,
176        })
177    }
178
179    fn read_sample(&self, file: &mut impl Read) -> io::Result<Event> {
180        let mut s = self.sample_id_format;
181        let identifier = bool_to_option!(s.contains(SampleFormat::IDENTIFIER), read_raw(file)?);
182        if let Some(id) = identifier.as_ref() {
183            if let Some(actual_format) = self.id_to_sample_format.get(id) {
184                s = *actual_format;
185            }
186        }
187        let ip = bool_to_option!(s.contains(SampleFormat::IP), read_raw(file)?);
188        let pid = bool_to_option!(s.contains(SampleFormat::TID), read_raw(file)?);
189        let tid = bool_to_option!(s.contains(SampleFormat::TID), read_raw(file)?);
190        let time = bool_to_option!(s.contains(SampleFormat::TIME), read_raw(file)?);
191        let addr = bool_to_option!(s.contains(SampleFormat::ADDR), read_raw(file)?);
192        let id = bool_to_option!(s.contains(SampleFormat::ID), read_raw(file)?);
193        let stream_id = bool_to_option!(s.contains(SampleFormat::STREAM_ID), read_raw(file)?);
194        let cpu = bool_to_option!(s.contains(SampleFormat::CPU), read_raw(file)?);
195        let res = bool_to_option!(s.contains(SampleFormat::CPU), read_raw(file)?);
196        let period = bool_to_option!(s.contains(SampleFormat::PERIOD), read_raw(file)?);
197        let call_chain: Vec<u64> = if !s.contains(SampleFormat::CALLCHAIN) {
198            vec![]
199        } else {
200            collect_n(
201                {
202                    let x: u64 = read_raw(file)?;
203                    x
204                } as usize,
205                || read_raw(file),
206            )?
207        };
208        use Event::Sample;
209        Ok(Sample {
210            identifier,
211            ip,
212            pid,
213            tid,
214            time,
215            addr,
216            id,
217            stream_id,
218            cpu,
219            res,
220            period,
221            call_chain,
222        })
223    }
224}
225
226pub fn read_events(
227    file: &mut File,
228    header: &PerfHeader,
229    event_desciptions: &[EventDescription],
230) -> Result<(Vec<Event>, u64, u64)> {
231    let sample_reader = SampleReader::new(event_desciptions)?;
232
233    let mut events = Vec::new();
234    let mut position = file.seek(io::SeekFrom::Start(header.data.offset))?;
235    let mut size = 0;
236    let mut start = std::u64::MAX;
237    let mut end = 0;
238    debug!("read events");
239    while size < header.data.size {
240        let event_header: EventHeader = read_raw(file)?;
241        debug!("{:x} {:?}", position, event_header);
242
243        match event_header.record_type {
244            RecordType::MMap => {
245                let part: MMapPart = read_raw(file)?;
246                let filename = read_string(
247                    file,
248                    event_header.size as usize
249                        - mem::size_of::<MMapPart>()
250                        - mem::size_of::<EventHeader>()
251                        - sample_reader.sample_id_size,
252                )?;
253                let s = sample_reader.read_sample_id(file)?;
254                events.push(Event::MMap {
255                    pid: part.pid,
256                    tid: part.tid,
257                    addr: part.addr,
258                    pgoff: part.pgoff,
259                    len: part.len,
260                    filename: filename,
261                    sample_id: s,
262                });
263            }
264            RecordType::Sample => {
265                let sample = sample_reader.read_sample(file)?;
266                if let Event::Sample {
267                    time: Some(time), ..
268                } = sample
269                {
270                    start = std::cmp::min(start, time);
271                    end = std::cmp::max(end, time);
272                }
273                events.push(sample);
274            }
275            RecordType::MMap2 => {
276                let part: MMap2Part = read_raw(file)?;
277                let filename = read_string(
278                    file,
279                    event_header.size as usize
280                        - mem::size_of::<MMap2Part>()
281                        - mem::size_of::<EventHeader>()
282                        - sample_reader.sample_id_size,
283                )?;
284                let s = sample_reader.read_sample_id(file)?;
285                events.push(Event::MMap2 {
286                    pid: part.pid,
287                    tid: part.tid,
288                    addr: part.addr,
289                    pgoff: part.pgoff,
290                    len: part.len,
291                    maj: part.maj,
292                    min: part.min,
293                    ino: part.ino,
294                    ino_generation: part.ino_generation,
295                    prot: part.prot,
296                    flags: part.flags,
297                    filename: filename,
298                    sample_id: s,
299                });
300            }
301            RecordType::Exit => {
302                let part: ExitPart = read_raw(file)?;
303                let s = sample_reader.read_sample_id(file)?;
304                events.push(Event::Exit {
305                    pid: part.pid,
306                    ppid: part.ppid,
307                    tid: part.tid,
308                    ptid: part.ptid,
309                    time: part.time,
310                    sample_id: s,
311                });
312                start = std::cmp::min(start, part.time);
313                end = std::cmp::max(end, part.time);
314            }
315            RecordType::Comm => {
316                let part: CommPart = read_raw(file)?;
317                let comm = read_string(
318                    file,
319                    event_header.size as usize
320                        - mem::size_of::<CommPart>()
321                        - mem::size_of::<EventHeader>()
322                        - sample_reader.sample_id_size,
323                )?;
324                let s = sample_reader.read_sample_id(file)?;
325                events.push(Event::Comm {
326                    pid: part.pid,
327                    tid: part.tid,
328                    comm: comm,
329                    sample_id: s,
330                });
331            }
332            RecordType::FinishedRound => events.push(Event::FinishedRound),
333            _ => events.push(Event::Unsupported),
334        }
335        size += event_header.size as u64;
336        position = file.seek(io::SeekFrom::Start(header.data.offset + size))?;
337    }
338    Ok((events, start, end))
339}