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::{AttributeDescription, NrCpus, PmuMappings, SampleTimeRange};
13use super::features::{Feature, FeatureSet};
14use super::simpleperf;
15
16pub struct PerfFile {
18 pub(crate) endian: Endianness,
19 pub(crate) features: FeatureSet,
20 pub(crate) feature_sections: LinearMap<Feature, Vec<u8>>,
21 pub(crate) attributes: Vec<AttributeDescription>,
23}
24
25impl PerfFile {
26 pub fn event_attributes(&self) -> &[AttributeDescription] {
28 &self.attributes
29 }
30 pub fn build_ids(&self) -> Result<HashMap<DsoKey, DsoInfo>, Error> {
60 let section_data = match self.feature_section_data(Feature::BUILD_ID) {
61 Some(section) => section,
62 None => return Ok(HashMap::new()),
63 };
64 let mut cursor = section_data;
65 let mut build_ids = HashMap::new();
66 loop {
67 let event = match self.endian {
68 Endianness::LittleEndian => BuildIdEvent::parse::<_, LittleEndian>(&mut cursor),
69 Endianness::BigEndian => BuildIdEvent::parse::<_, BigEndian>(&mut cursor),
70 };
71 let event = match event {
72 Ok(e) => e,
73 Err(_) => break,
74 };
75 let misc = event.header.misc;
76 let path = event.file_path;
77 let build_id = event.build_id;
78 let dso_key = match DsoKey::detect(&path, CpuMode::from_misc(misc)) {
79 Some(dso_key) => dso_key,
80 None => continue,
81 };
82 build_ids.insert(dso_key, DsoInfo { path, build_id });
83 }
84 Ok(build_ids)
85 }
86
87 pub fn sample_time_range(&self) -> Result<Option<SampleTimeRange>, Error> {
89 let section_data = match self.feature_section_data(Feature::SAMPLE_TIME) {
90 Some(section) => section,
91 None => return Ok(None),
92 };
93 let time_range = match self.endian {
94 Endianness::LittleEndian => SampleTimeRange::parse::<_, LittleEndian>(section_data)?,
95 Endianness::BigEndian => SampleTimeRange::parse::<_, BigEndian>(section_data)?,
96 };
97 Ok(Some(time_range))
98 }
99
100 fn feature_string(&self, feature: Feature) -> Result<Option<&str>, Error> {
102 match self.feature_section_data(feature) {
103 Some(section) => Ok(Some(self.read_string(section)?.0)),
104 None => Ok(None),
105 }
106 }
107
108 pub fn hostname(&self) -> Result<Option<&str>, Error> {
110 self.feature_string(Feature::HOSTNAME)
111 }
112
113 pub fn os_release(&self) -> Result<Option<&str>, Error> {
115 self.feature_string(Feature::OSRELEASE)
116 }
117
118 pub fn perf_version(&self) -> Result<Option<&str>, Error> {
121 self.feature_string(Feature::VERSION)
122 }
123
124 pub fn arch(&self) -> Result<Option<&str>, Error> {
126 self.feature_string(Feature::ARCH)
127 }
128
129 pub fn nr_cpus(&self) -> Result<Option<NrCpus>, Error> {
131 self.feature_section_data(Feature::NRCPUS)
132 .map(|section| {
133 Ok(match self.endian {
134 Endianness::LittleEndian => NrCpus::parse::<_, LittleEndian>(section),
135 Endianness::BigEndian => NrCpus::parse::<_, BigEndian>(section),
136 }?)
137 })
138 .transpose()
139 }
140
141 pub fn cpu_desc(&self) -> Result<Option<&str>, Error> {
144 self.feature_string(Feature::CPUDESC)
145 }
146
147 pub fn cpu_id(&self) -> Result<Option<&str>, Error> {
150 self.feature_string(Feature::CPUID)
151 }
152
153 pub fn is_stats(&self) -> bool {
155 self.features.has_feature(Feature::STAT)
156 }
157
158 pub fn cmdline(&self) -> Result<Option<Vec<&str>>, Error> {
160 match self.feature_section_data(Feature::CMDLINE) {
161 Some(section) => Ok(Some(self.read_string_list(section)?.0)),
162 None => Ok(None),
163 }
164 }
165
166 pub fn total_mem(&self) -> Result<Option<u64>, Error> {
168 let data = match self.feature_section_data(Feature::TOTAL_MEM) {
169 Some(data) => data,
170 None => return Ok(None),
171 };
172 if data.len() < 8 {
173 return Err(Error::FeatureSectionTooSmall);
174 }
175 let b = data;
176 let data = [b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]];
177 let mem = match self.endian {
178 Endianness::LittleEndian => u64::from_le_bytes(data),
179 Endianness::BigEndian => u64::from_be_bytes(data),
180 };
181 Ok(Some(mem))
182 }
183
184 pub fn simpleperf_meta_info(&self) -> Result<Option<HashMap<&str, &str>>, Error> {
186 match self.feature_section_data(Feature::SIMPLEPERF_META_INFO) {
187 Some(section) => Ok(Some(simpleperf::parse_meta_info_map(section)?)),
188 None => Ok(None),
189 }
190 }
191
192 pub fn simpleperf_symbol_tables(
198 &self,
199 ) -> Result<Option<Vec<simpleperf::SimpleperfFileRecord>>, Error> {
200 if let Some(section) = self.feature_section_data(Feature::SIMPLEPERF_FILE2) {
201 return Ok(Some(simpleperf::parse_file2_section(section, self.endian)?));
202 }
203 if let Some(section) = self.feature_section_data(Feature::SIMPLEPERF_FILE) {
204 return Ok(Some(simpleperf::parse_file_section(section, self.endian)?));
205 }
206 Ok(None)
207 }
208
209 pub fn pmu_mappings(&self) -> Result<Option<PmuMappings>, Error> {
229 self.feature_section_data(Feature::PMU_MAPPINGS)
230 .map(|section| {
231 Ok(match self.endian {
232 Endianness::LittleEndian => PmuMappings::parse::<_, LittleEndian>(section),
233 Endianness::BigEndian => PmuMappings::parse::<_, BigEndian>(section),
234 }?)
235 })
236 .transpose()
237 }
238
239 pub fn features(&self) -> FeatureSet {
241 self.features
242 }
243
244 pub fn feature_section_data(&self, feature: Feature) -> Option<&[u8]> {
246 self.feature_sections.get(&feature).map(Deref::deref)
247 }
248
249 pub fn endian(&self) -> Endianness {
251 self.endian
252 }
253
254 fn read_string<'s>(&self, s: &'s [u8]) -> Result<(&'s str, &'s [u8]), Error> {
255 if s.len() < 4 {
256 return Err(Error::NotEnoughSpaceForStringLen);
257 }
258 let (len_bytes, rest) = s.split_at(4);
259 let len_bytes = [len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]];
260 let len = match self.endian {
261 Endianness::LittleEndian => u32::from_le_bytes(len_bytes),
262 Endianness::BigEndian => u32::from_be_bytes(len_bytes),
263 };
264 let len = usize::try_from(len).map_err(|_| Error::StringLengthBiggerThanUsize)?;
265 if rest.len() < len {
266 return Err(Error::StringLengthTooLong);
267 }
268 let (s, rest) = rest.split_at(len);
269 let actual_len = memchr::memchr(0, s).unwrap_or(s.len());
270 let s = std::str::from_utf8(&s[..actual_len])?;
271 Ok((s, rest))
272 }
273
274 fn read_string_list<'s>(&self, s: &'s [u8]) -> Result<(Vec<&'s str>, &'s [u8]), Error> {
275 if s.len() < 4 {
276 return Err(Error::NotEnoughSpaceForStringListLen);
277 }
278 let (len_bytes, mut rest) = s.split_at(4);
279 let len_bytes = [len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]];
280 let len = match self.endian {
281 Endianness::LittleEndian => u32::from_le_bytes(len_bytes),
282 Endianness::BigEndian => u32::from_be_bytes(len_bytes),
283 };
284 let len = usize::try_from(len).map_err(|_| Error::StringListLengthBiggerThanUsize)?;
285 let mut vec = Vec::with_capacity(len);
286 for _ in 0..len {
287 let s;
288 (s, rest) = self.read_string(rest)?;
289 vec.push(s);
290 }
291
292 Ok((vec, rest))
293 }
294}