iptr_edge_analyzer/memory_reader/
perf_mmap.rs1use std::{
5 fs::File,
6 path::{Path, PathBuf},
7};
8
9use super::ReadMemory;
10use iptr_perf_pt_reader::PerfMmap2Header;
11use memmap2::{Mmap, MmapOptions};
12use thiserror::Error;
13
14pub struct PerfMmapBasedMemoryReader {
26 entries: Vec<MmappedEntry>,
28}
29
30pub struct MmappedEntry {
34 mmap: Mmap,
35 virtual_address: u64,
36}
37
38impl MmappedEntry {
39 #[must_use]
41 pub fn content(&self) -> &[u8] {
42 &self.mmap
43 }
44
45 #[must_use]
48 pub fn virtual_address(&self) -> u64 {
49 self.virtual_address
50 }
51}
52
53#[derive(Debug, Error)]
56pub enum PerfMmapBasedMemoryReaderError {
57 #[error("Not mmapped area {0:#x} accessed")]
59 NotMmapped(u64),
60}
61
62#[derive(Debug, Error)]
65pub enum PerfMmapBasedMemoryReaderCreateError {
66 #[error("Failed to open mmapped file {}: {source}", path.display())]
68 FileIo {
69 path: PathBuf,
71 #[source]
73 source: std::io::Error,
74 },
75 #[error("Target file {} is shorter than mapped moment: expected {expect_length} bytes, but got {real_length} bytes.", path.display())]
78 FileTooShort {
79 path: PathBuf,
81 expect_length: u64,
83 real_length: u64,
85 },
86}
87
88impl PerfMmapBasedMemoryReader {
89 #[expect(clippy::cast_possible_truncation)]
94 pub fn new(
95 mmap2_headers: &[PerfMmap2Header],
96 ) -> Result<Self, PerfMmapBasedMemoryReaderCreateError> {
97 let mut entries = Vec::with_capacity(mmap2_headers.len());
98
99 for mmap2_header in mmap2_headers {
100 let filename_path = Path::new(&mmap2_header.filename);
101 if !filename_path.is_absolute() {
102 log::warn!(
104 "Mmapped filename {} is not absolute path, skip.",
105 mmap2_header.filename
106 );
107 continue;
108 }
109 let file = File::open(filename_path).map_err(|io_err| {
110 PerfMmapBasedMemoryReaderCreateError::FileIo {
111 path: filename_path.to_path_buf(),
112 source: io_err,
113 }
114 })?;
115 let mmap_res = unsafe {
117 MmapOptions::default()
118 .len(mmap2_header.len as usize)
119 .offset(mmap2_header.pgoff)
120 .map(&file)
121 };
122 let mmap = mmap_res.map_err(|io_err| PerfMmapBasedMemoryReaderCreateError::FileIo {
123 path: filename_path.to_path_buf(),
124 source: io_err,
125 })?;
126 if mmap.len() as u64 != mmap2_header.len {
127 return Err(PerfMmapBasedMemoryReaderCreateError::FileTooShort {
128 path: filename_path.to_path_buf(),
129 expect_length: mmap2_header.len,
130 real_length: mmap.len() as u64,
131 });
132 }
133 log::trace!(
134 "Mmapped {:016x}--{:016x}\t{}",
135 mmap2_header.addr,
136 mmap2_header.addr.saturating_add(mmap2_header.len),
137 mmap2_header.filename
138 );
139 entries.push(MmappedEntry {
140 mmap,
141 virtual_address: mmap2_header.addr,
142 });
143 }
144
145 entries.sort_by_key(|entry| entry.virtual_address);
147
148 Ok(Self { entries })
149 }
150
151 #[must_use]
155 pub fn mmapped_entries(&self) -> &[MmappedEntry] {
156 &self.entries
157 }
158}
159
160impl ReadMemory for PerfMmapBasedMemoryReader {
161 type Error = PerfMmapBasedMemoryReaderError;
162
163 fn at_decode_begin(&mut self) -> Result<(), Self::Error> {
164 Ok(())
165 }
166
167 #[expect(clippy::cast_possible_truncation)]
168 fn read_memory<T>(
169 &mut self,
170 address: u64,
171 size: usize,
172 callback: impl FnOnce(&[u8]) -> T,
173 ) -> std::result::Result<T, Self::Error> {
174 let pos = match self
175 .entries
176 .binary_search_by_key(&address, |entry| entry.virtual_address)
177 {
178 Ok(pos) => pos,
179 Err(pos) => {
180 if pos == 0 {
181 return Err(PerfMmapBasedMemoryReaderError::NotMmapped(address));
182 }
183 pos - 1
184 }
185 };
186 debug_assert!(pos < self.entries.len(), "Unexpected pos out of bounds!");
188 let entry = unsafe { self.entries.get_unchecked(pos) };
189 let start_offset = address - entry.virtual_address;
190 let read_size = std::cmp::min(size, entry.mmap.len().saturating_sub(start_offset as usize));
191 if read_size == 0 {
192 return Err(PerfMmapBasedMemoryReaderError::NotMmapped(address));
193 }
194 let Some(mem) = entry
195 .mmap
196 .get((start_offset as usize)..((start_offset as usize).saturating_add(read_size)))
197 else {
198 return Err(PerfMmapBasedMemoryReaderError::NotMmapped(
199 address.saturating_add(read_size as u64) - 1,
200 ));
201 };
202 Ok(callback(mem))
203 }
204}