perf_event_data/records/
mmap2.rs

1use std::borrow::Cow;
2use std::ffi::OsStr;
3use std::fmt;
4
5use perf_event_open_sys::bindings;
6
7use crate::error::ParseError;
8use crate::prelude::*;
9use crate::Mmap;
10
11used_in_docs!(OsStr);
12
13/// MMAP2 events record memory mappings with extra info compared to MMAP
14/// records.
15///
16/// This struct corresponds to `PERF_RECORD_MMAP2`. See the [manpage] for more
17/// documentation here.
18///
19/// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
20#[derive(Clone)]
21pub struct Mmap2<'a> {
22    /// The process ID.
23    pub pid: u32,
24
25    /// The thread ID.
26    pub tid: u32,
27
28    /// The address that the mapping was placed at in the process' address
29    /// space.
30    pub addr: u64,
31
32    /// The length, in bytes, of the allocated memory.
33    pub len: u64,
34
35    /// The page offset of the memory mapping.
36    pub pgoff: u64,
37
38    /// Protection information for the mapping.
39    pub prot: u32,
40
41    /// Flags used when creating the mapping.
42    pub flags: u32,
43
44    /// The path to the file that is being mapped, if there is one.
45    ///
46    /// # Notes
47    /// - Not all memory mappings have a path on the file system. In cases where
48    ///   there is no such path then this will be a label(ish) string from the
49    ///   kernel (e.g. `[stack]`, `[heap]`, `[vdso]`, etc.)
50    /// - Just because the mapping has a path doesn't necessarily mean that the
51    ///   file at that path was the file that was mapped. The file may have been
52    ///   deleted in the meantime or the process may be under a chroot.
53    ///
54    /// If you need to be able to tell whether the file at the path is the same
55    /// one as was mapped you will need to use [`Mmap2`] instead.
56    pub filename: Cow<'a, [u8]>,
57
58    detail: MmapDetail,
59}
60
61#[derive(Clone)]
62enum MmapDetail {
63    Default {
64        maj: u32,
65        min: u32,
66        ino: u64,
67        ino_generation: u64,
68    },
69    BuildId {
70        build_id: [u8; 20],
71        len: u8,
72    },
73}
74
75impl<'a> Mmap2<'a> {
76    /// The path to the file that is being mapped, as an [`OsStr`].
77    ///
78    /// # Notes
79    /// - Not all memory mappings have a path on the file system. In cases where
80    ///   there is no such path then this will be a label(ish) string from the
81    ///   kernel (e.g. `[stack]`, `[heap]`, `[vdso]`, etc.)
82    /// - Just because the mapping has a path doesn't necessarily mean that the
83    ///   file at that path was the file that was mapped. The file may have been
84    ///   deleted in the meantime or the process may be under a chroot.
85    ///
86    /// If you need to be able to tell whether the file at the path is the same
87    /// one as was mapped you will need to use [`Mmap2`] instead.
88    #[cfg(unix)]
89    pub fn filename_os(&self) -> &OsStr {
90        use std::os::unix::ffi::OsStrExt;
91
92        OsStrExt::from_bytes(&self.filename)
93    }
94
95    /// The major ID of the underlying device of the fd being mapped.
96    pub fn maj(&self) -> Option<u32> {
97        match &self.detail {
98            MmapDetail::Default { maj, .. } => Some(*maj),
99            _ => None,
100        }
101    }
102
103    /// The minor ID of the underlying device of the fd being mapped.
104    pub fn min(&self) -> Option<u32> {
105        match &self.detail {
106            MmapDetail::Default { min, .. } => Some(*min),
107            _ => None,
108        }
109    }
110
111    /// The inode number.
112    pub fn ino(&self) -> Option<u64> {
113        match &self.detail {
114            MmapDetail::Default { ino, .. } => Some(*ino),
115            _ => None,
116        }
117    }
118
119    /// The inode generation.
120    pub fn ino_generation(&self) -> Option<u64> {
121        match &self.detail {
122            MmapDetail::Default { ino_generation, .. } => Some(*ino_generation),
123            _ => None,
124        }
125    }
126
127    /// The build id of the binary being mapped.
128    ///
129    /// This variant will only be generated if `build_id` was set when building
130    /// the counter.
131    pub fn build_id(&self) -> Option<&[u8]> {
132        match &self.detail {
133            MmapDetail::BuildId { build_id, len } => Some(&build_id[..*len as usize]),
134            _ => None,
135        }
136    }
137
138    /// Convert this record to a [`Mmap`] record.
139    pub fn to_mmap(&self) -> Mmap<'a> {
140        self.clone().into_mmap()
141    }
142
143    /// Convert this record to a [`Mmap`] record.
144    #[inline]
145    pub fn into_mmap(self) -> Mmap<'a> {
146        Mmap {
147            pid: self.pid,
148            tid: self.tid,
149            addr: self.addr,
150            len: self.len,
151            pgoff: self.pgoff,
152            filename: self.filename,
153        }
154    }
155
156    /// Convert all the borrowed data in this `Mmap2` into owned data.
157    pub fn into_owned(self) -> Mmap2<'static> {
158        Mmap2 {
159            filename: self.filename.into_owned().into(),
160            ..self
161        }
162    }
163}
164
165impl<'p> Parse<'p> for Mmap2<'p> {
166    fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
167    where
168        E: Endian,
169        B: ParseBuf<'p>,
170    {
171        Ok(Self {
172            pid: p.parse()?,
173            tid: p.parse()?,
174            addr: p.parse()?,
175            len: p.parse()?,
176            pgoff: p.parse()?,
177            detail: p.parse()?,
178            prot: p.parse()?,
179            flags: p.parse()?,
180            filename: p.parse_rest_trim_nul()?,
181        })
182    }
183}
184
185impl<'p> Parse<'p> for MmapDetail {
186    fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
187    where
188        E: Endian,
189        B: ParseBuf<'p>,
190    {
191        if p.config().misc() & bindings::PERF_RECORD_MISC_MMAP_BUILD_ID as u16 != 0 {
192            let len: u8 = p.parse()?;
193            let _ = p.parse_u8()?;
194            let _ = p.parse_u16()?;
195            let build_id = p.parse_array()?;
196
197            if len as usize > build_id.len() {
198                return Err(ParseError::custom(
199                    ErrorKind::InvalidRecord,
200                    format_args!("build_id had invalid length ({len} > 20)"),
201                ));
202            }
203
204            Ok(Self::BuildId { build_id, len })
205        } else {
206            Ok(Self::Default {
207                maj: p.parse()?,
208                min: p.parse()?,
209                ino: p.parse()?,
210                ino_generation: p.parse()?,
211            })
212        }
213    }
214}
215
216impl fmt::Debug for Mmap2<'_> {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        let mut dbg = f.debug_struct("Mmap2");
219        dbg.field("pid", &self.pid)
220            .field("tid", &self.tid)
221            .field("addr", &crate::util::fmt::HexAddr(self.addr))
222            .field("len", &self.len)
223            .field("pgoff", &self.pgoff);
224
225        match &self.detail {
226            MmapDetail::Default {
227                maj,
228                min,
229                ino,
230                ino_generation,
231            } => {
232                dbg.field("maj", maj)
233                    .field("min", min)
234                    .field("ino", ino)
235                    .field("ino_generation", ino_generation);
236            }
237            MmapDetail::BuildId { .. } => {
238                if let Some(build_id) = self.build_id() {
239                    dbg.field("build_id", &crate::util::fmt::HexStr(build_id));
240                }
241            }
242        }
243
244        dbg.field("prot", &self.prot)
245            .field("flags", &self.flags)
246            .field("filename", &crate::util::fmt::ByteStr(&self.filename));
247
248        dbg.finish()
249    }
250}