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, 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 simpleperf_meta_info(&self) -> Result<Option<HashMap<&str, &str>>, Error> {
218 match self.feature_section_data(Feature::SIMPLEPERF_META_INFO) {
219 Some(section) => Ok(Some(simpleperf::parse_meta_info_map(section)?)),
220 None => Ok(None),
221 }
222 }
223
224 pub fn simpleperf_symbol_tables(
230 &self,
231 ) -> Result<Option<Vec<simpleperf::SimpleperfFileRecord>>, Error> {
232 if let Some(section) = self.feature_section_data(Feature::SIMPLEPERF_FILE2) {
233 return Ok(Some(simpleperf::parse_file2_section(section, self.endian)?));
234 }
235 if let Some(section) = self.feature_section_data(Feature::SIMPLEPERF_FILE) {
236 return Ok(Some(simpleperf::parse_file_section(section, self.endian)?));
237 }
238 Ok(None)
239 }
240
241 pub fn pmu_mappings(&self) -> Result<Option<PmuMappings>, Error> {
261 self.feature_section_data(Feature::PMU_MAPPINGS)
262 .map(|section| {
263 Ok(match self.endian {
264 Endianness::LittleEndian => PmuMappings::parse::<_, LittleEndian>(section),
265 Endianness::BigEndian => PmuMappings::parse::<_, BigEndian>(section),
266 }?)
267 })
268 .transpose()
269 }
270
271 pub fn features(&self) -> FeatureSet {
273 self.features
274 }
275
276 pub fn feature_section_data(&self, feature: Feature) -> Option<&[u8]> {
278 self.feature_sections.get(&feature).map(Deref::deref)
279 }
280
281 pub fn endian(&self) -> Endianness {
283 self.endian
284 }
285
286 fn read_string<'s>(&self, s: &'s [u8]) -> Result<(&'s str, &'s [u8]), Error> {
287 if s.len() < 4 {
288 return Err(Error::NotEnoughSpaceForStringLen);
289 }
290 let (len_bytes, rest) = s.split_at(4);
291 let len_bytes = [len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]];
292 let len = match self.endian {
293 Endianness::LittleEndian => u32::from_le_bytes(len_bytes),
294 Endianness::BigEndian => u32::from_be_bytes(len_bytes),
295 };
296 let len = usize::try_from(len).map_err(|_| Error::StringLengthBiggerThanUsize)?;
297 if rest.len() < len {
298 return Err(Error::StringLengthTooLong);
299 }
300 let (s, rest) = rest.split_at(len);
301 let actual_len = memchr::memchr(0, s).unwrap_or(s.len());
302 let s = std::str::from_utf8(&s[..actual_len])?;
303 Ok((s, rest))
304 }
305
306 fn read_string_list<'s>(&self, s: &'s [u8]) -> Result<(Vec<&'s str>, &'s [u8]), Error> {
307 if s.len() < 4 {
308 return Err(Error::NotEnoughSpaceForStringListLen);
309 }
310 let (len_bytes, mut rest) = s.split_at(4);
311 let len_bytes = [len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]];
312 let len = match self.endian {
313 Endianness::LittleEndian => u32::from_le_bytes(len_bytes),
314 Endianness::BigEndian => u32::from_be_bytes(len_bytes),
315 };
316 let len = usize::try_from(len).map_err(|_| Error::StringListLengthBiggerThanUsize)?;
317 let mut vec = Vec::with_capacity(len);
318 for _ in 0..len {
319 let s;
320 (s, rest) = self.read_string(rest)?;
321 vec.push(s);
322 }
323
324 Ok((vec, rest))
325 }
326}