1use std::collections::HashMap;
2
3use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt};
4use linux_perf_event_reader::Endianness;
5use prost::Message;
6
7use crate::Error;
8
9pub struct SimplePerfEventType {
10 pub name: String,
11 pub type_: u64,
12 pub config: u64,
13}
14
15impl SimplePerfEventType {
16 pub fn new(name: String, type_: u64, config: u64) -> Self {
17 Self {
18 name,
19 type_,
20 config,
21 }
22 }
23}
24
25pub fn parse_meta_info_map(bytes: &[u8]) -> Result<HashMap<&str, &str>, std::str::Utf8Error> {
51 let iter = bytes.split(|c| *c == b'\0');
52 let keys = iter.clone().step_by(2);
53 let values = iter.skip(1).step_by(2);
54 let mut map = HashMap::new();
55 for (key, value) in keys.zip(values) {
56 let key = std::str::from_utf8(key)?;
57 let value = std::str::from_utf8(value)?;
58 map.insert(key, value);
59 }
60 Ok(map)
61}
62
63pub fn get_event_types(meta_info_map: &HashMap<&str, &str>) -> Option<Vec<SimplePerfEventType>> {
64 let event_type_info = meta_info_map.get("event_type_info")?;
65 let mut event_types = Vec::new();
66 for line in event_type_info.split('\n') {
67 let mut parts = line.split(',');
68 let name = parts.next()?.to_string();
69 let type_ = parts.next()?.parse().ok()?;
70 let config = parts.next()?.parse().ok()?;
71 event_types.push(SimplePerfEventType::new(name, type_, config));
72 }
73 Some(event_types)
74}
75
76pub mod simpleperf_dso_type {
78 pub const DSO_KERNEL: u32 = 0;
79 pub const DSO_KERNEL_MODULE: u32 = 1;
80 pub const DSO_ELF_FILE: u32 = 2;
81 pub const DSO_DEX_FILE: u32 = 3;
83 pub const DSO_SYMBOL_MAP_FILE: u32 = 4;
84 pub const DSO_UNKNOWN_FILE: u32 = 5;
85}
86
87#[derive(Clone, PartialEq, Eq, ::prost_derive::Message)]
91pub struct SimpleperfFileRecord {
92 #[prost(string, tag = "1")]
93 pub path: ::prost::alloc::string::String,
94 #[prost(uint32, tag = "2")]
96 pub r#type: u32,
97 #[prost(uint64, tag = "3")]
98 pub min_vaddr: u64,
99 #[prost(message, repeated, tag = "4")]
100 pub symbol: ::prost::alloc::vec::Vec<SimpleperfSymbol>,
101 #[prost(oneof = "SimpleperfTypeSpecificInfo", tags = "5, 6, 7")]
102 pub type_specific_msg: ::core::option::Option<SimpleperfTypeSpecificInfo>,
103}
104
105#[derive(Clone, PartialEq, Eq, ::prost_derive::Message)]
107pub struct SimpleperfSymbol {
108 #[prost(uint64, tag = "1")]
109 pub vaddr: u64,
110 #[prost(uint32, tag = "2")]
111 pub len: u32,
112 #[prost(string, tag = "3")]
113 pub name: ::prost::alloc::string::String,
114}
115
116#[derive(Clone, PartialEq, Eq, ::prost_derive::Message)]
118pub struct SimpleperfDexFileInfo {
119 #[prost(uint64, repeated, tag = "1")]
120 pub dex_file_offset: ::prost::alloc::vec::Vec<u64>,
121}
122
123#[derive(Clone, PartialEq, Eq, ::prost_derive::Message)]
125pub struct SimpleperfElfFileInfo {
126 #[prost(uint64, tag = "1")]
127 pub file_offset_of_min_vaddr: u64,
128}
129
130#[derive(Clone, PartialEq, Eq, ::prost_derive::Message)]
132pub struct SimpleperfKernelModuleInfo {
133 #[prost(uint64, tag = "1")]
134 pub memory_offset_of_min_vaddr: u64,
135}
136
137#[derive(Clone, PartialEq, Eq, ::prost_derive::Oneof)]
139pub enum SimpleperfTypeSpecificInfo {
140 #[prost(message, tag = "5")]
142 SimpleperfDexFileInfo(SimpleperfDexFileInfo),
143 #[prost(message, tag = "6")]
145 ElfFile(SimpleperfElfFileInfo),
146 #[prost(message, tag = "7")]
148 KernelModule(SimpleperfKernelModuleInfo),
149}
150
151pub fn parse_file2_section(
152 mut bytes: &[u8],
153 endian: Endianness,
154) -> Result<Vec<SimpleperfFileRecord>, Error> {
155 let mut files = Vec::new();
156 while !bytes.is_empty() {
160 let len = match endian {
161 Endianness::LittleEndian => bytes.read_u32::<LittleEndian>()?,
162 Endianness::BigEndian => bytes.read_u32::<BigEndian>()?,
163 };
164 let len = len as usize;
165 let file_data = bytes.get(..len).ok_or(Error::FeatureSectionTooSmall)?;
166 bytes = &bytes[len..];
167 let file = SimpleperfFileRecord::decode(file_data)
168 .map_err(Error::ProtobufParsingSimpleperfFileSection)?;
169 files.push(file);
170 }
171 Ok(files)
172}
173
174pub fn parse_file_section(
181 mut bytes: &[u8],
182 endian: Endianness,
183) -> Result<Vec<SimpleperfFileRecord>, Error> {
184 let mut files = Vec::new();
185 while !bytes.is_empty() {
189 let len = match endian {
190 Endianness::LittleEndian => bytes.read_u32::<LittleEndian>()?,
191 Endianness::BigEndian => bytes.read_u32::<BigEndian>()?,
192 };
193 let len = len as usize;
194 let file_data = bytes.get(..len).ok_or(Error::FeatureSectionTooSmall)?;
195 bytes = &bytes[len..];
196 let file_result = match endian {
197 Endianness::LittleEndian => SimpleperfFileRecord::decode_v1::<LittleEndian>(file_data),
198 Endianness::BigEndian => SimpleperfFileRecord::decode_v1::<BigEndian>(file_data),
199 };
200 let file = file_result.map_err(Error::ParsingSimpleperfFileV1Section)?;
201 files.push(file);
202 }
203 Ok(files)
204}
205
206impl SimpleperfFileRecord {
207 pub fn decode_v1<T: ByteOrder>(mut data: &[u8]) -> Result<Self, std::io::Error> {
208 let path = data.read_nul_terminated_str()?.to_owned();
209 let file_type = data.read_u32::<T>()?;
210 if file_type > simpleperf_dso_type::DSO_UNKNOWN_FILE {
211 return Err(std::io::Error::new(
212 std::io::ErrorKind::InvalidData,
213 format!("unknown file type for {path} in file feature section: {file_type}"),
214 ));
215 }
216 let min_vaddr = data.read_u64::<T>()?;
217 let symbol_count = data.read_u32::<T>()? as usize;
218 if symbol_count > data.len() {
219 return Err(std::io::Error::new(
220 std::io::ErrorKind::InvalidData,
221 format!(
222 "Unreasonable symbol count for {path} in file feature section: {symbol_count}"
223 ),
224 ));
225 }
226 let mut symbols = Vec::with_capacity(symbol_count);
227 for _ in 0..symbol_count {
228 let vaddr = data.read_u64::<T>()?;
229 let len = data.read_u32::<T>()?;
230 let name = data.read_nul_terminated_str()?.to_owned();
231 symbols.push(SimpleperfSymbol { vaddr, len, name });
232 }
233 let type_specific_msg = match file_type {
234 simpleperf_dso_type::DSO_DEX_FILE => {
235 let offset_count = data.read_u32::<T>()? as usize;
236 if offset_count > data.len() {
237 return Err(std::io::Error::new(
238 std::io::ErrorKind::InvalidData,
239 format!("Unreasonable symbol count for {path} in file feature section: {offset_count}"),
240 ));
241 }
242 let mut dex_file_offset = Vec::with_capacity(offset_count);
243 for _ in 0..offset_count {
244 dex_file_offset.push(data.read_u64::<T>()?);
245 }
246 Some(SimpleperfTypeSpecificInfo::SimpleperfDexFileInfo(
247 SimpleperfDexFileInfo { dex_file_offset },
248 ))
249 }
250 simpleperf_dso_type::DSO_ELF_FILE => {
251 let file_offset_of_min_vaddr = if data.is_empty() {
252 u64::MAX
253 } else {
254 data.read_u64::<T>()?
255 };
256 Some(SimpleperfTypeSpecificInfo::ElfFile(SimpleperfElfFileInfo {
257 file_offset_of_min_vaddr,
258 }))
259 }
260 simpleperf_dso_type::DSO_KERNEL_MODULE => {
261 let memory_offset_of_min_vaddr = if data.is_empty() {
262 u64::MAX
263 } else {
264 data.read_u64::<T>()?
265 };
266 Some(SimpleperfTypeSpecificInfo::KernelModule(
267 SimpleperfKernelModuleInfo {
268 memory_offset_of_min_vaddr,
269 },
270 ))
271 }
272 _ => None,
273 };
274
275 Ok(Self {
276 path,
277 r#type: file_type,
278 min_vaddr,
279 symbol: symbols,
280 type_specific_msg,
281 })
282 }
283}
284
285trait SliceReadStringExt<'a> {
286 fn read_nul_terminated_str(&mut self) -> std::io::Result<&'a str>;
287}
288
289impl<'a> SliceReadStringExt<'a> for &'a [u8] {
290 fn read_nul_terminated_str(&mut self) -> std::io::Result<&'a str> {
291 let Some(len) = memchr::memchr(0, self) else {
292 return Err(std::io::Error::new(
293 std::io::ErrorKind::UnexpectedEof,
294 "Nul terminator not found",
295 ));
296 };
297 let s = std::str::from_utf8(&self[..len])
298 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
299 *self = &self[(len + 1)..];
300 Ok(s)
301 }
302}