1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use std::borrow::Cow;
use std::ffi::OsStr;

use crate::prelude::*;
use crate::Mmap2;

/// MMAP events record memory mappings.
///
/// This struct corresponds to `PERF_RECORD_MMAP`. See the [manpage] for more
/// documentation here.
///
/// [manpage]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html
#[derive(Clone, Debug)]
pub struct Mmap<'a> {
    /// The process ID.
    pub pid: u32,

    /// The thread ID.
    pub tid: u32,

    /// The address that the mapping was placed at in the process' address
    /// space.
    pub addr: u64,

    /// The length, in bytes, of the allocated memory.
    pub len: u64,

    /// The page offset of the memory mapping.
    pub pgoff: u64,

    /// The path to the file that is being mapped, if there is one.
    ///
    /// # Notes
    /// - Not all memory mappings have a path on the file system. In cases where
    ///   there is no such path then this will be a label(ish) string from the
    ///   kernel (e.g. `[stack]`, `[heap]`, `[vdso]`, etc.)
    /// - Just because the mapping has a path doesn't necessarily mean that the
    ///   file at that path was the file that was mapped. The file may have been
    ///   deleted in the meantime or the process may be under a chroot.
    ///
    /// If you need to be able to tell whether the file at the path is the same
    /// one as was mapped you will need to use [`Mmap2`] instead.
    pub filename: Cow<'a, [u8]>,
}

impl<'a> Mmap<'a> {
    /// The path to the file that is being mapped, as an [`OsStr`].
    ///
    /// # Notes
    /// - Not all memory mappings have a path on the file system. In cases where
    ///   there is no such path then this will be a label(ish) string from the
    ///   kernel (e.g. `[stack]`, `[heap]`, `[vdso]`, etc.)
    /// - Just because the mapping has a path doesn't necessarily mean that the
    ///   file at that path was the file that was mapped. The file may have been
    ///   deleted in the meantime or the process may be under a chroot.
    ///
    /// If you need to be able to tell whether the file at the path is the same
    /// one as was mapped you will need to use [`Mmap2`] instead.
    #[cfg(unix)]
    pub fn filename_os(&self) -> &OsStr {
        use std::os::unix::ffi::OsStrExt;

        OsStrExt::from_bytes(&self.filename)
    }

    /// Convert all the borrowed data in this `Mmap` into owned data.
    pub fn into_owned(self) -> Mmap<'static> {
        Mmap {
            filename: self.filename.into_owned().into(),
            ..self
        }
    }
}

impl<'p> Parse<'p> for Mmap<'p> {
    fn parse<B, E>(p: &mut Parser<B, E>) -> ParseResult<Self>
    where
        E: Endian,
        B: ParseBuf<'p>,
    {
        Ok(Self {
            pid: p.parse()?,
            tid: p.parse()?,
            addr: p.parse()?,
            len: p.parse()?,
            pgoff: p.parse()?,
            filename: p.parse_rest_trim_nul()?,
        })
    }
}

impl<'a> From<Mmap2<'a>> for Mmap<'a> {
    fn from(value: Mmap2<'a>) -> Self {
        value.into_mmap()
    }
}

#[cfg(test)]
mod tests {
    use crate::endian::Little;

    use super::*;

    #[test]
    fn test_parse() {
        let bytes: &[u8] = &[
            10, 100, 0, 0, 11, 100, 0, 0, 0, 160, 118, 129, 189, 127, 0, 0, 0, 16, 0, 0, 0, 0, 0,
            0, 0, 160, 118, 129, 189, 127, 0, 0, 47, 47, 97, 110, 111, 110, 0, 0,
        ];

        let mut parser: Parser<_, Little> = Parser::new(bytes, ParseConfig::default());
        let mmap: Mmap = parser.parse().unwrap();

        assert_eq!(mmap.pid, 25610);
        assert_eq!(mmap.tid, 25611);
        assert_eq!(mmap.addr, 0x7FBD8176A000);
        assert_eq!(mmap.len, 4096);
        assert_eq!(mmap.pgoff, 0x7FBD8176A000);
        assert_eq!(&*mmap.filename, b"//anon");
    }
}