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
52pub struct HeaderString;
53
54impl HeaderString {
55 pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Option<String>, std::io::Error> {
57 let len = reader.read_u32::<T>()?;
58 let mut s = vec![0; len as usize];
59 reader.read_exact(&mut s)?;
60 let actual_len = memchr::memchr(0, &s).unwrap_or(s.len());
61 s.truncate(actual_len);
62 Ok(String::from_utf8(s).ok())
63 }
64}
65
66#[derive(Debug, Clone)]
68pub struct AttributeDescription {
69 pub attr: PerfEventAttr,
70 pub name: Option<String>,
71 pub event_ids: Vec<u64>,
72}
73
74impl AttributeDescription {
75 pub fn parse_event_desc_section<C: Read + Seek, T: ByteOrder>(
77 mut cursor: C,
78 ) -> Result<Vec<Self>, Error> {
79 let nr = cursor.read_u32::<T>()?;
92 let mut attributes = Vec::with_capacity(nr as usize);
93 let attr_size = cursor.read_u32::<T>()? as u64;
94 for _ in 0..nr {
95 let attr = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
96 let nr_ids = cursor.read_u32::<T>()?;
97 let event_string = HeaderString::parse::<_, T>(&mut cursor)?;
98 let mut ids = Vec::with_capacity(nr_ids as usize);
99 for _ in 0..nr_ids {
100 ids.push(cursor.read_u64::<T>()?);
101 }
102 attributes.push(AttributeDescription {
103 attr,
104 name: event_string,
105 event_ids: ids,
106 });
107 }
108 Ok(attributes)
109 }
110
111 pub fn parse_event_types_section<C: Read + Seek, T: ByteOrder>(
115 cursor: C,
116 event_types_section: &PerfFileSection,
117 attr_size: u64,
118 ) -> Result<Vec<Self>, Error> {
119 Self::parse_sequence_of_attr_and_id_section::<C, T>(
122 cursor,
123 event_types_section,
124 attr_size,
125 None,
126 )
127 }
128
129 pub fn parse_simpleperf_attr_section<C: Read + Seek, T: ByteOrder>(
133 cursor: C,
134 attr_section: &PerfFileSection,
135 attr_size: u64,
136 event_types: &[SimplePerfEventType],
137 ) -> Result<Vec<Self>, Error> {
138 if attr_size < PerfFileSection::STRUCT_SIZE {
139 return Err(ReadError::PerfEventAttr.into());
140 }
141 let attr_size_without_id_section = attr_size - PerfFileSection::STRUCT_SIZE;
148 let event_names: Vec<_> = event_types.iter().map(|t| t.name.as_str()).collect();
149 Self::parse_sequence_of_attr_and_id_section::<C, T>(
150 cursor,
151 attr_section,
152 attr_size_without_id_section,
153 Some(&event_names),
154 )
155 }
156
157 fn parse_sequence_of_attr_and_id_section<C: Read + Seek, T: ByteOrder>(
159 mut cursor: C,
160 section: &PerfFileSection,
161 attr_size: u64,
162 event_names: Option<&[&str]>,
163 ) -> Result<Vec<Self>, Error> {
164 cursor.seek(SeekFrom::Start(section.offset))?;
165
166 let entry_size = attr_size + PerfFileSection::STRUCT_SIZE;
168 let entry_count = section.size / entry_size;
169 let mut perf_event_event_type_info = Vec::with_capacity(entry_count as usize);
170 for _ in 0..entry_count {
171 let attr = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
172 let event_ids = PerfFileSection::parse::<_, T>(&mut cursor)?;
173 perf_event_event_type_info.push((attr, event_ids));
174 }
175
176 let mut attributes = Vec::new();
178 for (event_index, (attr, section)) in perf_event_event_type_info.into_iter().enumerate() {
179 cursor.seek(SeekFrom::Start(section.offset))?;
180 let id_count = section.size / 8;
182 let mut event_ids = Vec::with_capacity(id_count as usize);
183 for _ in 0..id_count {
184 event_ids.push(cursor.read_u64::<T>()?);
185 }
186 let name = if let Some(names) = event_names {
187 names.get(event_index).map(|s| s.to_string())
188 } else {
189 None
190 };
191 attributes.push(AttributeDescription {
192 attr,
193 name,
194 event_ids,
195 });
196 }
197 Ok(attributes)
198 }
199
200 pub fn parse_attr_section<C: Read + Seek, T: ByteOrder>(
205 mut cursor: C,
206 attr_section: &PerfFileSection,
207 attr_size: u64,
208 ) -> Result<Vec<Self>, Error> {
209 cursor.seek(SeekFrom::Start(attr_section.offset))?;
210 let attr_count = attr_section.size / attr_size;
211 let mut attributes = Vec::with_capacity(attr_count as usize);
212 for _ in 0..attr_count {
213 let attr = Self::parse_single_attr::<_, T>(&mut cursor, attr_size)?;
214 attributes.push(AttributeDescription {
215 attr,
216 name: None,
217 event_ids: vec![],
218 });
219 }
220 Ok(attributes)
221 }
222
223 fn parse_single_attr<C: Read + Seek, T: ByteOrder>(
224 mut cursor: C,
225 attr_size: u64,
226 ) -> Result<PerfEventAttr, Error> {
227 let (attr, size) =
228 PerfEventAttr::parse::<_, T>(&mut cursor).map_err(|_| ReadError::PerfEventAttr)?;
229 if size > attr_size {
230 return Err(Error::InconsistentAttributeSizes(size, attr_size));
231 }
232 if size < attr_size {
233 let remaining_bytes = attr_size - size;
234 cursor.seek(SeekFrom::Current(remaining_bytes as i64))?;
235 }
236 Ok(attr)
237 }
238
239 pub fn attributes(&self) -> &PerfEventAttr {
241 &self.attr
242 }
243
244 pub fn name(&self) -> Option<&str> {
246 self.name.as_deref()
247 }
248
249 pub fn ids(&self) -> &[u64] {
251 &self.event_ids
252 }
253}
254
255pub struct PmuMappings(pub LinearMap<u32, String>);
261
262impl PmuMappings {
263 pub fn parse<R: Read, T: ByteOrder>(mut reader: R) -> Result<Self, std::io::Error> {
264 let nr = reader.read_u32::<T>()?;
272 let mut vec = Vec::with_capacity(nr as usize);
273 for _ in 0..nr {
274 let pmu_type = reader.read_u32::<T>()?;
275 if let Some(pmu_name) = HeaderString::parse::<_, T>(&mut reader)? {
276 vec.push((pmu_type, pmu_name));
277 }
278 }
279 vec.sort_by_key(|item| item.0);
280 Ok(Self(vec.into_iter().collect()))
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, Eq)]
287pub struct ClockData {
288 pub clockid: u32,
290 pub wall_clock_ns: u64,
292 pub clockid_time_ns: u64,
294}
295
296impl ClockData {
297 pub const STRUCT_SIZE: usize = 4 + 4 + 8 + 8;
298
299 pub fn parse<R: Read, T: ByteOrder>(mut data: R) -> Result<Self, std::io::Error> {
300 let version = data.read_u32::<T>()?;
301 if version != 1 {
302 return Err(std::io::Error::new(
303 std::io::ErrorKind::InvalidData,
304 format!("Unsupported clock data version: {version}"),
305 ));
306 }
307 let clockid = data.read_u32::<T>()?;
308 let wall_clock_ns = data.read_u64::<T>()?;
309 let clockid_time_ns = data.read_u64::<T>()?;
310
311 Ok(Self {
312 clockid,
313 wall_clock_ns,
314 clockid_time_ns,
315 })
316 }
317}