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, 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}