Skip to main content

linux_perf_data/
feature_sections.rs

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/// The number of available and online CPUs. (`nr_cpus`)
12#[derive(Debug, Clone, Copy)]
13pub struct NrCpus {
14    /// CPUs not yet onlined
15    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/// The timestamps of the first and last sample.
33#[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/// Information about compression used in the perf.data file.
53#[derive(Debug, Clone, Copy)]
54pub struct CompressionInfo {
55    pub version: u32,
56    /// Compression algorithm type. 1 = Zstd
57    pub type_: u32,
58    /// Compression level (e.g., 1-22 for Zstd)
59    pub level: u32,
60    /// Compression ratio achieved
61    pub ratio: u32,
62    /// mmap buffer size
63    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    /// Parse a string.
90    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/// A single event attr with name and corresponding event IDs.
101#[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    /// Parse the `HEADER_EVENT_DESC` section of a perf.data file into a Vec of `AttributeDescription` structs.
110    pub fn parse_event_desc_section<C: Read + Seek, T: ByteOrder>(
111        mut cursor: C,
112    ) -> Result<Vec<Self>, Error> {
113        // ```c
114        // struct {
115        //   uint32_t nr; /* number of events */
116        //   uint32_t attr_size; /* size of each perf_event_attr */
117        //   struct {
118        //     struct perf_event_attr attr;  /* size of attr_size */
119        //     uint32_t nr_ids;
120        //     struct perf_header_string event_string;
121        //     uint64_t ids[nr_ids];
122        //   } events[nr]; /* Variable length records */
123        // };
124        // ```
125        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    /// Parse the `event_types` section of a perf.data file into a Vec of `AttributeDescription` structs.
146    /// This section was used in the past but is no longer used.
147    /// Only call this function if event_types_section.size is non-zero.
148    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        // In the event_types section, each attribute takes up attr_size bytes and is followed
154        // by a PerfFileSection struct (16 bytes).
155        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    /// Parse the `attr` section of a perf.data file into a Vec of `AttributeDescription` structs,
164    /// for files from Simpleperf. These files pack event ID information into the `attr` section
165    /// and contain event names in the `SIMPLEPERF_META_INFO` section.
166    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        // Simpleperf reports an attr_size which is 16 bytes larger than the size that's used
176        // for the perf_event_attr data. These 16 extra bytes carry the (offset, size) of the
177        // per-event event IDs section.
178        // So the format of the attr section in the simpleperf is very similar to the format of the
179        // event_types section in old perf.data files, with the only difference being that the
180        // id_section information is "inside" the attr_size rather than outside it.
181        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    /// Used for parsing the `event_types` section (old Linux perf) and for parsing the `attr` section (Simpleperf).
192    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        // Each entry in the event_types section is a PerfEventAttr followed by a PerfFileSection.
201        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        // Read the lists of event IDs for each event type.
211        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            // This section is just a list of u64 event IDs.
215            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    /// Parse the `attr` section of a perf.data file into a Vec of `AttributeDescription` structs.
235    /// This section is used as a last resort because it does not have any
236    /// information about event IDs. If multiple events are observed, we will
237    /// not be able to know which event record belongs to which attr.
238    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    /// The event attributes.
274    pub fn attributes(&self) -> &PerfEventAttr {
275        &self.attr
276    }
277
278    /// The event name.
279    pub fn name(&self) -> Option<&str> {
280        self.name.as_deref()
281    }
282
283    /// The IDs for this event.
284    pub fn ids(&self) -> &[u64] {
285        &self.event_ids
286    }
287}
288
289/// The names of the dynamic PMU types used in [`PerfEventType::DynamicPmu`](linux_perf_event_reader::PerfEventType::DynamicPmu).
290///
291/// For example, this allows you to find out whether a `DynamicPmu`
292/// perf event is a kprobe or a uprobe, which then lets you interpret
293/// the meaning of the config fields.
294pub 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        // struct {
299        //     uint32_t nr;
300        //     struct pmu {
301        //        uint32_t pmu_type;
302        //        struct perf_header_string pmu_name;
303        //     } [nr]; /* Variable length records */
304        // };
305        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/// The clock data header contains information about the clock used to
319/// record timestamps in the perf.data file.
320#[derive(Debug, Clone, PartialEq, Eq)]
321pub struct ClockData {
322    /// Clock ID used for timestamps
323    pub clockid: u32,
324    /// Wall clock time in nanoseconds since Unix epoch.
325    pub wall_clock_ns: u64,
326    /// Clock ID time in nanoseconds at the same instant as `wall_clock_ns`.
327    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}