1use std::io::{Read, Seek, SeekFrom};
2
3use byteorder::{ByteOrder, ReadBytesExt};
4use linear_map::LinearMap;
5use linux_perf_event_reader::PerfEventAttr;
6
7use super::section::PerfFileSection;
8use crate::simpleperf::SimplePerfEventType;
9use crate::{Error, ReadError};
10
11#[derive(Debug, Clone, Copy)]
13pub struct NrCpus {
14 pub nr_cpus_available: u32,
16 pub nr_cpus_online: u32,
17}
18
19impl NrCpus {
20 pub const STRUCT_SIZE: usize = 4 + 4;
21
22 pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, std::io::Error> {
23 let nr_cpus_available = reader.read_u32::<T>()?;
24 let nr_cpus_online = reader.read_u32::<T>()?;
25 Ok(Self {
26 nr_cpus_available,
27 nr_cpus_online,
28 })
29 }
30}
31
32#[derive(Debug, Clone, Copy)]
34pub struct SampleTimeRange {
35 pub first_sample_time: u64,
36 pub last_sample_time: u64,
37}
38
39impl SampleTimeRange {
40 pub const STRUCT_SIZE: usize = 8 + 8;
41
42 pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, std::io::Error> {
43 let first_sample_time = reader.read_u64::<T>()?;
44 let last_sample_time = reader.read_u64::<T>()?;
45 Ok(Self {
46 first_sample_time,
47 last_sample_time,
48 })
49 }
50}
51
52#[derive(Debug, Clone, Copy)]
54pub struct CompressionInfo {
55 pub version: u32,
56 pub type_: u32,
58 pub level: u32,
60 pub ratio: u32,
62 pub mmap_len: u32,
64}
65
66impl CompressionInfo {
67 pub const STRUCT_SIZE: usize = 4 + 4 + 4 + 4 + 4;
68 pub const ZSTD_TYPE: u32 = 1;
69
70 pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, std::io::Error> {
71 let version = reader.read_u32::<T>()?;
72 let type_ = reader.read_u32::<T>()?;
73 let level = reader.read_u32::<T>()?;
74 let ratio = reader.read_u32::<T>()?;
75 let mmap_len = reader.read_u32::<T>()?;
76 Ok(Self {
77 version,
78 type_,
79 level,
80 ratio,
81 mmap_len,
82 })
83 }
84}
85
86pub struct HeaderString;
87
88impl HeaderString {
89 pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Option<String>, std::io::Error> {
91 let len = reader.read_u32::<T>()?;
92 let mut s = vec![0; len as usize];
93 reader.read_exact(&mut s)?;
94 let actual_len = memchr::memchr(0, &s).unwrap_or(s.len());
95 s.truncate(actual_len);
96 Ok(String::from_utf8(s).ok())
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct AttributeDescription {
103 pub attr: PerfEventAttr,
104 pub name: Option<String>,
105 pub event_ids: Vec<u64>,
106}
107
108impl AttributeDescription {
109 pub fn parse_event_desc_section<C: Read + Seek, T: ByteOrder>(
111 mut cursor: C,
112 ) -> Result<Vec<Self>, Error> {
113 let nr = cursor.read_u32::<T>()?;
126 let mut attributes = Vec::with_capacity(nr as usize);
127 let attr_size = cursor.read_u32::<T>()? as u64;
128 for _ in 0..nr {
129 let attr = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
130 let nr_ids = cursor.read_u32::<T>()?;
131 let event_string = HeaderString::parse::<_, T>(&mut cursor)?;
132 let mut ids = Vec::with_capacity(nr_ids as usize);
133 for _ in 0..nr_ids {
134 ids.push(cursor.read_u64::<T>()?);
135 }
136 attributes.push(AttributeDescription {
137 attr,
138 name: event_string,
139 event_ids: ids,
140 });
141 }
142 Ok(attributes)
143 }
144
145 pub fn parse_event_types_section<C: Read + Seek, T: ByteOrder>(
149 cursor: C,
150 event_types_section: &PerfFileSection,
151 attr_size: u64,
152 ) -> Result<Vec<Self>, Error> {
153 Self::parse_sequence_of_attr_and_id_section::<C, T>(
156 cursor,
157 event_types_section,
158 attr_size,
159 None,
160 )
161 }
162
163 pub fn parse_simpleperf_attr_section<C: Read + Seek, T: ByteOrder>(
167 cursor: C,
168 attr_section: &PerfFileSection,
169 attr_size: u64,
170 event_types: &[SimplePerfEventType],
171 ) -> Result<Vec<Self>, Error> {
172 if attr_size < PerfFileSection::STRUCT_SIZE {
173 return Err(ReadError::PerfEventAttr.into());
174 }
175 let attr_size_without_id_section = attr_size - PerfFileSection::STRUCT_SIZE;
182 let event_names: Vec<_> = event_types.iter().map(|t| t.name.as_str()).collect();
183 Self::parse_sequence_of_attr_and_id_section::<C, T>(
184 cursor,
185 attr_section,
186 attr_size_without_id_section,
187 Some(&event_names),
188 )
189 }
190
191 fn parse_sequence_of_attr_and_id_section<C: Read + Seek, T: ByteOrder>(
193 mut cursor: C,
194 section: &PerfFileSection,
195 attr_size: u64,
196 event_names: Option<&[&str]>,
197 ) -> Result<Vec<Self>, Error> {
198 cursor.seek(SeekFrom::Start(section.offset))?;
199
200 let entry_size = attr_size + PerfFileSection::STRUCT_SIZE;
202 let entry_count = section.size / entry_size;
203 let mut perf_event_event_type_info = Vec::with_capacity(entry_count as usize);
204 for _ in 0..entry_count {
205 let attr = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
206 let event_ids = PerfFileSection::parse::<_, T>(&mut cursor)?;
207 perf_event_event_type_info.push((attr, event_ids));
208 }
209
210 let mut attributes = Vec::new();
212 for (event_index, (attr, section)) in perf_event_event_type_info.into_iter().enumerate() {
213 cursor.seek(SeekFrom::Start(section.offset))?;
214 let id_count = section.size / 8;
216 let mut event_ids = Vec::with_capacity(id_count as usize);
217 for _ in 0..id_count {
218 event_ids.push(cursor.read_u64::<T>()?);
219 }
220 let name = if let Some(names) = event_names {
221 names.get(event_index).map(|s| s.to_string())
222 } else {
223 None
224 };
225 attributes.push(AttributeDescription {
226 attr,
227 name,
228 event_ids,
229 });
230 }
231 Ok(attributes)
232 }
233
234 pub fn parse_attr_section<C: Read + Seek, T: ByteOrder>(
239 mut cursor: C,
240 attr_section: &PerfFileSection,
241 attr_size: u64,
242 ) -> Result<Vec<Self>, Error> {
243 cursor.seek(SeekFrom::Start(attr_section.offset))?;
244 let attr_count = attr_section.size / attr_size;
245 let mut attributes = Vec::with_capacity(attr_count as usize);
246 for _ in 0..attr_count {
247 let attr = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
248 attributes.push(AttributeDescription {
249 attr,
250 name: None,
251 event_ids: vec![],
252 });
253 }
254 Ok(attributes)
255 }
256
257 fn parse_single_attr<C: Read + Seek, T: ByteOrder>(
258 mut cursor: C,
259 attr_size: u64,
260 ) -> Result<PerfEventAttr, Error> {
261 let (attr, size) =
262 PerfEventAttr::parse::<_, T>(&mut cursor).map_err(|_| ReadError::PerfEventAttr)?;
263 if size > attr_size {
264 return Err(Error::InconsistentAttributeSizes(size, attr_size));
265 }
266 if size < attr_size {
267 let remaining_bytes = attr_size - size;
268 cursor.seek(SeekFrom::Current(remaining_bytes as i64))?;
269 }
270 Ok(attr)
271 }
272
273 pub fn attributes(&self) -> &PerfEventAttr {
275 &self.attr
276 }
277
278 pub fn name(&self) -> Option<&str> {
280 self.name.as_deref()
281 }
282
283 pub fn ids(&self) -> &[u64] {
285 &self.event_ids
286 }
287}
288
289pub struct PmuMappings(pub LinearMap<u32, String>);
295
296impl PmuMappings {
297 pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, std::io::Error> {
298 let nr = reader.read_u32::<T>()?;
306 let mut vec = Vec::with_capacity(nr as usize);
307 for _ in 0..nr {
308 let pmu_type = reader.read_u32::<T>()?;
309 if let Some(pmu_name) = HeaderString::parse::<_, T>(&mut reader)? {
310 vec.push((pmu_type, pmu_name));
311 }
312 }
313 vec.sort_by_key(|item| item.0);
314 Ok(Self(vec.into_iter().collect()))
315 }
316}
317
318#[derive(Debug, Clone, PartialEq, Eq)]
321pub struct ClockData {
322 pub clockid: u32,
324 pub wall_clock_ns: u64,
326 pub clockid_time_ns: u64,
328}
329
330impl ClockData {
331 pub const STRUCT_SIZE: usize = 4 + 4 + 8 + 8;
332
333 pub fn parse<R: Read, T: ByteOrder>(mut data: R) -> Result<Self, std::io::Error> {
334 let version = data.read_u32::<T>()?;
335 if version != 1 {
336 return Err(std::io::Error::new(
337 std::io::ErrorKind::InvalidData,
338 format!("Unsupported clock data version: {version}"),
339 ));
340 }
341 let clockid = data.read_u32::<T>()?;
342 let wall_clock_ns = data.read_u64::<T>()?;
343 let clockid_time_ns = data.read_u64::<T>()?;
344
345 Ok(Self {
346 clockid,
347 wall_clock_ns,
348 clockid_time_ns,
349 })
350 }
351}