1use byteorder::{BigEndian, LittleEndian};
2use linear_map::LinearMap;
3use linux_perf_event_reader::{CpuMode, Endianness};
4
5use std::collections::HashMap;
6use std::ops::Deref;
7
8use super::build_id_event::BuildIdEvent;
9use super::dso_info::DsoInfo;
10use super::dso_key::DsoKey;
11use super::error::Error;
12use super::feature_sections::{
13 AttributeDescription, ClockData, CompressionInfo, NrCpus, PmuMappings, SampleTimeRange,
14};
15use super::features::{Feature, FeatureSet};
16use super::simpleperf;
17
18pub struct PerfFile {
20 pub(crate) endian: Endianness,
21 pub(crate) features: FeatureSet,
22 pub(crate) feature_sections: LinearMap<Feature, Vec<u8>>,
23 pub(crate) attributes: Vec<AttributeDescription>,
25}
26
27impl PerfFile {
28 pub fn event_attributes(&self) -> &[AttributeDescription] {
30 &self.attributes
31 }
32 pub fn build_ids(&self) -> Result<HashMap<DsoKey, DsoInfo>, Error> {
62 let section_data = match self.feature_section_data(Feature::BUILD_ID) {
63 Some(section) => section,
64 None => return Ok(HashMap::new()),
65 };
66 let mut cursor = section_data;
67 let mut build_ids = HashMap::new();
68 loop {
69 let event = match self.endian {
70 Endianness::LittleEndian => BuildIdEvent::parse::<_, LittleEndian>(&mut cursor),
71 Endianness::BigEndian => BuildIdEvent::parse::<_, BigEndian>(&mut cursor),
72 };
73 let event = match event {
74 Ok(e) => e,
75 Err(_) => break,
76 };
77 let misc = event.header.misc;
78 let path = event.file_path;
79 let build_id = event.build_id;
80 let dso_key = match DsoKey::detect(&path, CpuMode::from_misc(misc)) {
81 Some(dso_key) => dso_key,
82 None => continue,
83 };
84 build_ids.insert(dso_key, DsoInfo { path, build_id });
85 }
86 Ok(build_ids)
87 }
88
89 pub fn sample_time_range(&self) -> Result<Option<SampleTimeRange>, Error> {
91 let section_data = match self.feature_section_data(Feature::SAMPLE_TIME) {
92 Some(section) => section,
93 None => return Ok(None),
94 };
95 let time_range = match self.endian {
96 Endianness::LittleEndian => SampleTimeRange::parse::<_, LittleEndian>(section_data)?,
97 Endianness::BigEndian => SampleTimeRange::parse::<_, BigEndian>(section_data)?,
98 };
99 Ok(Some(time_range))
100 }
101
102 fn feature_string(&self, feature: Feature) -> Result<Option<&str>, Error> {
104 match self.feature_section_data(feature) {
105 Some(section) => Ok(Some(self.read_string(section)?.0)),
106 None => Ok(None),
107 }
108 }
109
110 pub fn hostname(&self) -> Result<Option<&str>, Error> {
112 self.feature_string(Feature::HOSTNAME)
113 }
114
115 pub fn os_release(&self) -> Result<Option<&str>, Error> {
117 self.feature_string(Feature::OSRELEASE)
118 }
119
120 pub fn perf_version(&self) -> Result<Option<&str>, Error> {
123 self.feature_string(Feature::VERSION)
124 }
125
126 pub fn arch(&self) -> Result<Option<&str>, Error> {
128 self.feature_string(Feature::ARCH)
129 }
130
131 pub fn nr_cpus(&self) -> Result<Option<NrCpus>, Error> {
133 self.feature_section_data(Feature::NRCPUS)
134 .map(|section| {
135 Ok(match self.endian {
136 Endianness::LittleEndian => NrCpus::parse::<_, LittleEndian>(section),
137 Endianness::BigEndian => NrCpus::parse::<_, BigEndian>(section),
138 }?)
139 })
140 .transpose()
141 }
142
143 pub fn cpu_desc(&self) -> Result<Option<&str>, Error> {
146 self.feature_string(Feature::CPUDESC)
147 }
148
149 pub fn cpu_id(&self) -> Result<Option<&str>, Error> {
152 self.feature_string(Feature::CPUID)
153 }
154
155 pub fn is_stats(&self) -> bool {
157 self.features.has_feature(Feature::STAT)
158 }
159
160 pub fn cmdline(&self) -> Result<Option<Vec<&str>>, Error> {
162 match self.feature_section_data(Feature::CMDLINE) {
163 Some(section) => Ok(Some(self.read_string_list(section)?.0)),
164 None => Ok(None),
165 }
166 }
167
168 pub fn total_mem(&self) -> Result<Option<u64>, Error> {
170 let data = match self.feature_section_data(Feature::TOTAL_MEM) {
171 Some(data) => data,
172 None => return Ok(None),
173 };
174 if data.len() < 8 {
175 return Err(Error::FeatureSectionTooSmall);
176 }
177 let b = data;
178 let data = [b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]];
179 let mem = match self.endian {
180 Endianness::LittleEndian => u64::from_le_bytes(data),
181 Endianness::BigEndian => u64::from_be_bytes(data),
182 };
183 Ok(Some(mem))
184 }
185
186 pub fn clock_frequency(&self) -> Result<Option<u64>, Error> {
188 let data = match self.feature_section_data(Feature::CLOCKID) {
189 Some(data) => data,
190 None => return Ok(None),
191 };
192 if data.len() < 8 {
193 return Err(Error::FeatureSectionTooSmall);
194 }
195 let b = data;
196 let data = [b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]];
197 let mem = match self.endian {
198 Endianness::LittleEndian => u64::from_le_bytes(data),
199 Endianness::BigEndian => u64::from_be_bytes(data),
200 };
201 Ok(Some(mem))
202 }
203
204 pub fn clock_data(&self) -> Result<Option<ClockData>, Error> {
206 self.feature_section_data(Feature::CLOCK_DATA)
207 .map(|section| {
208 Ok(match self.endian {
209 Endianness::LittleEndian => ClockData::parse::<_, LittleEndian>(section),
210 Endianness::BigEndian => ClockData::parse::<_, BigEndian>(section),
211 }?)
212 })
213 .transpose()
214 }
215
216 pub fn compression_info(&self) -> Result<Option<CompressionInfo>, Error> {
218 self.feature_section_data(Feature::COMPRESSED)
219 .map(|section| {
220 Ok(match self.endian {
221 Endianness::LittleEndian => CompressionInfo::parse::<_, LittleEndian>(section),
222 Endianness::BigEndian => CompressionInfo::parse::<_, BigEndian>(section),
223 }?)
224 })
225 .transpose()
226 }
227
228 pub fn simpleperf_meta_info(&self) -> Result<Option<HashMap<&str, &str>>, Error> {
230 match self.feature_section_data(Feature::SIMPLEPERF_META_INFO) {
231 Some(section) => Ok(Some(simpleperf::parse_meta_info_map(section)?)),
232 None => Ok(None),
233 }
234 }
235
236 pub fn simpleperf_symbol_tables(
242 &self,
243 ) -> Result<Option<Vec<simpleperf::SimpleperfFileRecord>>, Error> {
244 if let Some(section) = self.feature_section_data(Feature::SIMPLEPERF_FILE2) {
245 return Ok(Some(simpleperf::parse_file2_section(section, self.endian)?));
246 }
247 if let Some(section) = self.feature_section_data(Feature::SIMPLEPERF_FILE) {
248 return Ok(Some(simpleperf::parse_file_section(section, self.endian)?));
249 }
250 Ok(None)
251 }
252
253 pub fn pmu_mappings(&self) -> Result<Option<PmuMappings>, Error> {
273 self.feature_section_data(Feature::PMU_MAPPINGS)
274 .map(|section| {
275 Ok(match self.endian {
276 Endianness::LittleEndian => PmuMappings::parse::<_, LittleEndian>(section),
277 Endianness::BigEndian => PmuMappings::parse::<_, BigEndian>(section),
278 }?)
279 })
280 .transpose()
281 }
282
283 pub fn features(&self) -> FeatureSet {
285 self.features
286 }
287
288 pub fn feature_section_data(&self, feature: Feature) -> Option<&[u8]> {
290 self.feature_sections.get(&feature).map(Deref::deref)
291 }
292
293 pub fn endian(&self) -> Endianness {
295 self.endian
296 }
297
298 fn read_string<'s>(&self, s: &'s [u8]) -> Result<(&'s str, &'s [u8]), Error> {
299 if s.len() < 4 {
300 return Err(Error::NotEnoughSpaceForStringLen);
301 }
302 let (len_bytes, rest) = s.split_at(4);
303 let len_bytes = [len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]];
304 let len = match self.endian {
305 Endianness::LittleEndian => u32::from_le_bytes(len_bytes),
306 Endianness::BigEndian => u32::from_be_bytes(len_bytes),
307 };
308 let len = usize::try_from(len).map_err(|_| Error::StringLengthBiggerThanUsize)?;
309 if rest.len() < len {
310 return Err(Error::StringLengthTooLong);
311 }
312 let (s, rest) = rest.split_at(len);
313 let actual_len = memchr::memchr(0, s).unwrap_or(s.len());
314 let s = std::str::from_utf8(&s[..actual_len])?;
315 Ok((s, rest))
316 }
317
318 fn read_string_list<'s>(&self, s: &'s [u8]) -> Result<(Vec<&'s str>, &'s [u8]), Error> {
319 if s.len() < 4 {
320 return Err(Error::NotEnoughSpaceForStringListLen);
321 }
322 let (len_bytes, mut rest) = s.split_at(4);
323 let len_bytes = [len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]];
324 let len = match self.endian {
325 Endianness::LittleEndian => u32::from_le_bytes(len_bytes),
326 Endianness::BigEndian => u32::from_be_bytes(len_bytes),
327 };
328 let len = usize::try_from(len).map_err(|_| Error::StringListLengthBiggerThanUsize)?;
329 let mut vec = Vec::with_capacity(len);
330 for _ in 0..len {
331 let s;
332 (s, rest) = self.read_string(rest)?;
333 vec.push(s);
334 }
335
336 Ok((vec, rest))
337 }
338}