Created
July 23, 2020 01:29
-
-
Save dillonhicks/213fc66c64b8cc6b6831bfb81e03d0ed to your computer and use it in GitHub Desktop.
Parsing Linux Process PageMaps in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::{fmt, mem}; | |
| use std::convert::TryFrom; | |
| use std::io::{BufRead, BufReader, Read, Seek}; | |
| use std::path::{Path, PathBuf}; | |
| use crate::deps::structopt::StructOpt; | |
| use crate::deps::derive_more; | |
| use crate::deps::serde; | |
| use crate::deps::thiserror; | |
| use crate::deps::log::warn; | |
| pub(crate) mod deps { | |
| pub use derive_more; | |
| pub use serde; | |
| pub use thiserror; | |
| pub use log; | |
| pub use structopt; | |
| } | |
| fn proc_maps_path(pid: usize) -> PathBuf { | |
| Path::new("/") | |
| .join("proc") | |
| .join(pid.to_string()) | |
| .join("maps") | |
| } | |
| fn proc_pagemaps_path(pid: usize) -> PathBuf { | |
| Path::new("/") | |
| .join("proc") | |
| .join(pid.to_string()) | |
| .join("pagemap") | |
| } | |
| #[derive(thiserror::Error, Debug)] | |
| pub enum Error { | |
| #[error("an io error occurred {_0:}")] | |
| IO(#[from] std::io::Error), | |
| #[error("parsing {typename} from {value:?}, reason: {reason:}")] | |
| Parse { | |
| value: String, | |
| typename: &'static str, | |
| reason: String, | |
| }, | |
| #[error("unknown error")] | |
| Unknown, | |
| } | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)] | |
| pub enum Perm { | |
| Read, | |
| Write, | |
| Execute, | |
| Private, | |
| Shared, | |
| Nil, | |
| } | |
| impl Perm { | |
| pub fn to_char(self) -> char { | |
| use Perm::*; | |
| match self { | |
| Read => 'r', | |
| Write => 'w', | |
| Execute => 'x', | |
| Private => 'p', | |
| Shared => 's', | |
| Nil => '-' | |
| } | |
| } | |
| } | |
| impl TryFrom<char> for Perm { | |
| type Error = Error; | |
| fn try_from(ch: char) -> Result<Self, Self::Error> { | |
| use Perm::*; | |
| let perm = match ch.to_ascii_lowercase() { | |
| 'r' => Read, | |
| 'w' => Write, | |
| 'x' => Execute, | |
| 's' => Shared, | |
| 'p' => Private, | |
| '-' => Nil, | |
| _ => return Err(Error::Parse { | |
| value: ch.to_string(), | |
| typename: std::any::type_name::<Perm>(), | |
| reason: "character was not one of \"rwxsp\"".to_string(), | |
| }) | |
| }; | |
| Ok(perm) | |
| } | |
| } | |
| impl<'a> TryFrom<&'a str> for Perm { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let trimmed = value.trim(); | |
| if trimmed.is_empty() { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Perm>(), | |
| reason: "blank string".to_string(), | |
| }); | |
| } else if trimmed.len() != 1 { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Perm>(), | |
| reason: "string was longer than one character".to_string(), | |
| }); | |
| } | |
| let ch = trimmed.chars().next().unwrap(); | |
| TryFrom::try_from(ch) | |
| } | |
| } | |
| impl fmt::Display for Perm { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| use Perm::*; | |
| self.to_char().fmt(f) | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)] | |
| pub struct Device { | |
| major: usize, | |
| minor: usize, | |
| } | |
| impl Device { | |
| const SEPARATOR: char = ':'; | |
| } | |
| impl<'a> TryFrom<&'a str> for Device { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let trimmed = value.trim(); | |
| if trimmed.is_empty() { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Device>(), | |
| reason: "blank string".to_string(), | |
| }); | |
| } else if trimmed.len() < 3 { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Device>(), | |
| reason: "device string was shorter than the minimum number of characters (3)".to_string(), | |
| }); | |
| } | |
| let parts = trimmed.splitn(2, Device::SEPARATOR) | |
| .map(|s| usize::from_str_radix(s, 16)) | |
| .collect::<Vec<_>>(); | |
| if parts.len() != 2 { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Device>(), | |
| reason: format!("device string was not in the form XX{}YY", Device::SEPARATOR), | |
| }); | |
| } else if parts.iter().any(Result::is_err) { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Device>(), | |
| reason: format!("part of device string was not a number {:?}", parts), | |
| }); | |
| } | |
| let mut parts_iter = parts.into_iter().map(Result::<usize, _>::unwrap); | |
| let major = parts_iter.next().unwrap(); | |
| let minor = parts_iter.next().unwrap(); | |
| Ok( | |
| Device { | |
| major, | |
| minor, | |
| } | |
| ) | |
| } | |
| } | |
| impl fmt::Display for Device { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| write!(f, "{:0>2x}{}{:0>2x}", self.major, Device::SEPARATOR, self.minor) | |
| } | |
| } | |
| #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)] | |
| pub enum PathName { | |
| Empty, | |
| Pseudo(String), | |
| Real(String), | |
| } | |
| impl PathName { | |
| pub fn as_str(&self) -> &str { | |
| use PathName::*; | |
| match self { | |
| Empty => "", | |
| Real(s) | Pseudo(s) => s.as_str(), | |
| } | |
| } | |
| } | |
| impl<'a> TryFrom<&'a str> for PathName { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let trimmed = value.trim(); | |
| let path = if trimmed.is_empty() { | |
| PathName::Empty | |
| } else if trimmed.starts_with('[') && trimmed.ends_with(']') { | |
| PathName::Pseudo(trimmed.to_string()) | |
| } else { | |
| PathName::Real(trimmed.to_string()) | |
| }; | |
| Ok(path) | |
| } | |
| } | |
| impl fmt::Display for PathName { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| self.as_str().fmt(f) | |
| } | |
| } | |
| #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)] | |
| pub struct PermSet(Vec<Perm>); | |
| impl<'a> TryFrom<&'a str> for PermSet { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let trimmed = value.trim(); | |
| if trimmed.is_empty() { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<PermSet>(), | |
| reason: "blank string".to_string(), | |
| }); | |
| } | |
| let mut set = Vec::with_capacity(4); | |
| for ch in trimmed.chars() { | |
| set.push(Perm::try_from(ch)?); | |
| } | |
| Ok(PermSet(set.into_iter().collect())) | |
| } | |
| } | |
| impl fmt::Display for PermSet { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| for perm in self.0.iter() { | |
| perm.fmt(f)?; | |
| } | |
| Ok(()) | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Ord, Eq, Hash, serde::Serialize, serde::Deserialize)] | |
| pub struct AddressRange { | |
| low: usize, | |
| high: usize, | |
| } | |
| impl AddressRange { | |
| const SEPARATOR: char = '-'; | |
| pub fn contains(&self, n: usize) -> bool { | |
| (n >= self.low) && (n < self.high) | |
| } | |
| } | |
| impl<'a> TryFrom<&'a str> for AddressRange { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let trimmed = value.trim(); | |
| if trimmed.is_empty() { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<AddressRange>(), | |
| reason: "blank string".to_string(), | |
| }); | |
| } else if trimmed.len() < 3 { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<AddressRange>(), | |
| reason: "address range string was shorter than the minimum number of characters (3)".to_string(), | |
| }); | |
| } | |
| let parts = trimmed.splitn(2, AddressRange::SEPARATOR) | |
| .map(|s| usize::from_str_radix(s, 16)) | |
| .collect::<Vec<_>>(); | |
| if parts.len() != 2 { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<AddressRange>(), | |
| reason: format!("address range string was not in the form XX{}YY, parts={:?}", AddressRange::SEPARATOR, parts), | |
| }); | |
| } else if parts.iter().any(Result::is_err) { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<AddressRange>(), | |
| reason: format!("part of address range string was not a number {:?}", parts), | |
| }); | |
| } | |
| let mut parts_iter = parts.into_iter().map(Result::<usize, _>::unwrap); | |
| let low = parts_iter.next().unwrap(); | |
| let high = parts_iter.next().unwrap(); | |
| Ok( | |
| AddressRange { | |
| low, | |
| high, | |
| } | |
| ) | |
| } | |
| } | |
| impl fmt::Display for AddressRange { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| write!(f, "{:0>8x}{}{:0>8x}", self.low, AddressRange::SEPARATOR, self.high) | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Hash, derive_more::From, derive_more::Into, serde::Serialize, serde::Deserialize)] | |
| pub struct Offset(usize); | |
| impl<'a> TryFrom<&'a str> for Offset { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let trimmed = value.trim(); | |
| if trimmed.is_empty() { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Offset>(), | |
| reason: "blank string".to_string(), | |
| }); | |
| } | |
| Ok(Offset(usize::from_str_radix(trimmed, 16).map_err(|_err| { | |
| Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Offset>(), | |
| reason: "Offset string was not valid base 16 usize".to_string(), | |
| } | |
| })?)) | |
| } | |
| } | |
| impl fmt::Display for Offset { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| write!(f, "{:0>8x}", self.0) | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord, Hash, derive_more::Display, derive_more::From, derive_more::Into, serde::Serialize, serde::Deserialize)] | |
| pub struct Inode(usize); | |
| impl<'a> TryFrom<&'a str> for Inode { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let trimmed = value.trim(); | |
| if trimmed.is_empty() { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Inode>(), | |
| reason: "blank string".to_string(), | |
| }); | |
| } | |
| Ok(Inode(trimmed.parse::<usize>().map_err(|_err| { | |
| Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Inode>(), | |
| reason: "Inode string was not valid base 10 usize".to_string(), | |
| } | |
| })?)) | |
| } | |
| } | |
| /// | |
| /// ```text | |
| /// /proc/[pid]/maps | |
| /// A file containing the currently mapped memory regions and | |
| /// their access permissions. See mmap(2) for some further infor‐ | |
| /// mation about memory mappings. | |
| /// | |
| /// Permission to access this file is governed by a ptrace access | |
| /// mode PTRACE_MODE_READ_FSCREDS check; see ptrace(2). | |
| /// | |
| /// The format of the file is: | |
| /// | |
| /// address perms offset dev inode pathname | |
| /// 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon | |
| /// 00651000-00652000 r--p 00051000 08:02 173521 /usr/bin/dbus-daemon | |
| /// 00652000-00655000 rw-p 00052000 08:02 173521 /usr/bin/dbus-daemon | |
| /// 00e03000-00e24000 rw-p 00000000 00:00 0 [heap] | |
| /// 00e24000-011f7000 rw-p 00000000 00:00 0 [heap] | |
| /// ... | |
| /// 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so | |
| /// 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so | |
| /// 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so | |
| /// 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 | |
| /// 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so | |
| /// 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so | |
| /// 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so | |
| /// 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so | |
| /// ... | |
| /// f2c6ff8c000-7f2c7078c000 rw-p 00000000 00:00 0 [stack:986] | |
| /// ... | |
| /// 7fffb2c0d000-7fffb2c2e000 rw-p 00000000 00:00 0 [stack] | |
| /// 7fffb2d48000-7fffb2d49000 r-xp 00000000 00:00 0 [vdso] | |
| /// | |
| /// The address field is the address space in the process that the | |
| /// mapping occupies. The perms field is a set of permissions: | |
| /// | |
| /// r = read | |
| /// w = write | |
| /// x = execute | |
| /// s = shared | |
| /// p = private (copy on write) | |
| /// | |
| /// The offset field is the offset into the file/whatever; dev is | |
| /// the device (major:minor); inode is the inode on that device. | |
| /// 0 indicates that no inode is associated with the memory | |
| /// region, as would be the case with BSS (uninitialized data). | |
| /// | |
| /// The pathname field will usually be the file that is backing | |
| /// the mapping. For ELF files, you can easily coordinate with | |
| /// the offset field by looking at the Offset field in the ELF | |
| /// program headers (readelf -l). | |
| /// | |
| /// There are additional helpful pseudo-paths: | |
| /// | |
| /// [stack] | |
| /// The initial process's (also known as the main thread's) | |
| /// stack. | |
| /// | |
| /// [stack:<tid>] (from Linux 3.4 to 4.4) | |
| /// A thread's stack (where the <tid> is a thread ID). It | |
| /// corresponds to the /proc/[pid]/task/[tid]/ path. This | |
| /// field was removed in Linux 4.5, since providing this | |
| /// information for a process with large numbers of threads | |
| /// is expensive. | |
| /// | |
| /// [vdso] The virtual dynamically linked shared object. See | |
| /// vdso(7). | |
| /// | |
| /// [heap] The process's heap. | |
| /// | |
| /// If the pathname field is blank, this is an anonymous mapping | |
| /// as obtained via mmap(2). There is no easy way to coordinate | |
| /// this back to a process's source, short of running it through | |
| /// gdb(1), strace(1), or similar. | |
| /// | |
| /// pathname is shown unescaped except for newline characters, | |
| /// which are replaced with an octal escape sequence. As a | |
| /// result, it is not possible to determine whether the original | |
| /// pathname contained a newline character or the literal \e012 | |
| /// character sequence. | |
| /// | |
| /// If the mapping is file-backed and the file has been deleted, | |
| /// the string " (deleted)" is appended to the pathname. Note | |
| /// that this is ambiguous too. | |
| /// | |
| /// Under Linux 2.0, there is no field giving pathname. | |
| /// ``` | |
| #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] | |
| pub struct Region { | |
| addr_range: AddressRange, | |
| perms: PermSet, | |
| offset: Offset, | |
| device: Device, | |
| inode: Inode, | |
| pathname: PathName, | |
| extra: Vec<String>, | |
| } | |
| impl fmt::Display for Region { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| let s = format!("{} {} {} {} {}", self.addr_range, self.perms, self.offset, self.device, self.inode); | |
| s.fmt(f)?; | |
| match &self.pathname { | |
| PathName::Empty => Ok(()), | |
| path => { | |
| let pad = 73usize.checked_sub(s.len()).unwrap_or(0); | |
| " ".chars().cycle().take(pad).collect::<String>().fmt(f)?; | |
| self.pathname.fmt(f) | |
| } | |
| } | |
| } | |
| } | |
| impl<'a> TryFrom<&'a str> for Region { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let trimmed = value.trim(); | |
| if trimmed.is_empty() { | |
| return Err(Error::Parse { | |
| value: value.to_string(), | |
| typename: std::any::type_name::<Region>(), | |
| reason: "blank string".to_string(), | |
| }); | |
| } | |
| let mut iter = trimmed.split_ascii_whitespace(); | |
| let addr_range = AddressRange::try_from(iter.next().unwrap_or(""))?; | |
| let perms = PermSet::try_from(iter.next().unwrap_or(""))?; | |
| let offset = Offset::try_from(iter.next().unwrap_or(""))?; | |
| let device = Device::try_from(iter.next().unwrap_or(""))?; | |
| let inode = Inode::try_from(iter.next().unwrap_or(""))?; | |
| let pathname = PathName::try_from(iter.next().unwrap_or(""))?; | |
| // extra garbage we couldn't parse | |
| let extra = iter.map(str::to_string).collect::<Vec<_>>(); | |
| if !extra.is_empty() { | |
| warn!("dudebro, bad stuff happened while parsing, {:?}", extra); | |
| } | |
| Ok(Region { | |
| addr_range, | |
| perms, | |
| offset, | |
| device, | |
| inode, | |
| pathname, | |
| extra, | |
| }) | |
| } | |
| } | |
| #[derive(Debug, Clone, PartialEq)] | |
| pub struct Maps { | |
| map: std::collections::BTreeMap<usize, Region>, | |
| pathname_index: std::collections::HashMap<String, std::collections::BTreeSet<AddressRange>>, | |
| } | |
| impl Maps { | |
| fn new() -> Self { | |
| Self { | |
| map: std::collections::BTreeMap::new(), | |
| pathname_index: std::collections::HashMap::new(), | |
| } | |
| } | |
| fn insert(&mut self, entry: Region) { | |
| let addr_range = entry.addr_range; | |
| let pathname = entry.pathname.clone(); | |
| self.map.insert(addr_range.low, entry); | |
| self.pathname_index.entry(pathname.as_str().to_string()).or_default().insert(addr_range); | |
| } | |
| pub fn iter(&self) -> std::collections::btree_map::Iter<'_, usize, Region> { | |
| self.map.iter() | |
| } | |
| } | |
| impl<'a> TryFrom<&'a str> for Maps { | |
| type Error = Error; | |
| fn try_from(value: &'a str) -> Result<Self, Self::Error> { | |
| let mut pagemap = Maps::new(); | |
| for line in value.lines() { | |
| let entry = Region::try_from(line)?; | |
| pagemap.insert(entry); | |
| } | |
| Ok(pagemap) | |
| } | |
| } | |
| impl<'a, R> TryFrom<&'a mut BufReader<R>> for Maps where R: Read { | |
| type Error = Error; | |
| fn try_from(reader: &'a mut BufReader<R>) -> Result<Self, Self::Error> { | |
| let mut pagemap = Maps::new(); | |
| for line in reader.lines().map(|r| r.unwrap()) { | |
| let entry = Region::try_from(line.as_str())?; | |
| pagemap.insert(entry); | |
| } | |
| Ok(pagemap) | |
| } | |
| } | |
| impl fmt::Display for Maps { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| for value in self.map.values() { | |
| writeln!(f, "{}", value); | |
| } | |
| Ok(()) | |
| } | |
| } | |
| /// ```text | |
| /// pagemap, from the userspace perspective | |
| /// --------------------------------------- | |
| /// | |
| /// pagemap is a new (as of 2.6.25) set of interfaces in the kernel that allow | |
| /// userspace programs to examine the page tables and related information by | |
| /// reading files in /proc. | |
| /// | |
| /// There are four components to pagemap: | |
| /// | |
| /// * /proc/pid/pagemap. This file lets a userspace process find out which | |
| /// physical frame each virtual page is mapped to. It contains one 64-bit | |
| /// value for each virtual page, containing the following data (from | |
| /// fs/proc/task_mmu.c, above pagemap_read): | |
| /// | |
| /// * Bits 0-54 page frame number (PFN) if present | |
| /// * Bits 0-4 swap type if swapped | |
| /// * Bits 5-54 swap offset if swapped | |
| /// * Bit 55 pte is soft-dirty (see Documentation/vm/soft-dirty.txt) | |
| /// * Bit 56 page exclusively mapped (since 4.2) | |
| /// * Bits 57-60 zero | |
| /// * Bit 61 page is file-page or shared-anon (since 3.5) | |
| /// * Bit 62 page swapped | |
| /// * Bit 63 page present | |
| /// | |
| /// Since Linux 4.0 only users with the CAP_SYS_ADMIN capability can get PFNs. | |
| /// In 4.0 and 4.1 opens by unprivileged fail with -EPERM. Starting from | |
| /// 4.2 the PFN field is zeroed if the user does not have CAP_SYS_ADMIN. | |
| /// Reason: information about PFNs helps in exploiting Rowhammer vulnerability. | |
| /// | |
| /// If the page is not present but in swap, then the PFN contains an | |
| /// encoding of the swap file number and the page's offset into the | |
| /// swap. Unmapped pages return a null PFN. This allows determining | |
| /// precisely which pages are mapped (or in swap) and comparing mapped | |
| /// pages between processes. | |
| /// | |
| /// Efficient users of this interface will use /proc/pid/maps to | |
| /// determine which areas of memory are actually mapped and llseek to | |
| /// skip over unmapped regions. | |
| /// | |
| /// * /proc/kpagecount. This file contains a 64-bit count of the number of | |
| /// times each page is mapped, indexed by PFN. | |
| /// | |
| /// * /proc/kpageflags. This file contains a 64-bit set of flags for each | |
| /// page, indexed by PFN. | |
| /// | |
| /// The flags are (from fs/proc/page.c, above kpageflags_read): | |
| /// | |
| /// 0. LOCKED | |
| /// 1. ERROR | |
| /// 2. REFERENCED | |
| /// 3. UPTODATE | |
| /// 4. DIRTY | |
| /// 5. LRU | |
| /// 6. ACTIVE | |
| /// 7. SLAB | |
| /// 8. WRITEBACK | |
| /// 9. RECLAIM | |
| /// 10. BUDDY | |
| /// 11. MMAP | |
| /// 12. ANON | |
| /// 13. SWAPCACHE | |
| /// 14. SWAPBACKED | |
| /// 15. COMPOUND_HEAD | |
| /// 16. COMPOUND_TAIL | |
| /// 17. HUGE | |
| /// 18. UNEVICTABLE | |
| /// 19. HWPOISON | |
| /// 20. NOPAGE | |
| /// 21. KSM | |
| /// 22. THP | |
| /// 23. BALLOON | |
| /// 24. ZERO_PAGE | |
| /// 25. IDLE | |
| /// | |
| /// * /proc/kpagecgroup. This file contains a 64-bit inode number of the | |
| /// memory cgroup each page is charged to, indexed by PFN. Only available when | |
| /// CONFIG_MEMCG is set. | |
| /// | |
| /// Short descriptions to the page flags: | |
| /// | |
| /// 0. LOCKED | |
| /// page is being locked for exclusive access, eg. by undergoing read/write IO | |
| /// | |
| /// 7. SLAB | |
| /// page is managed by the SLAB/SLOB/SLUB/SLQB kernel memory allocator | |
| /// When compound page is used, SLUB/SLQB will only set this flag on the head | |
| /// page; SLOB will not flag it at all. | |
| /// | |
| /// 10. BUDDY | |
| /// a free memory block managed by the buddy system allocator | |
| /// The buddy system organizes free memory in blocks of various orders. | |
| /// An order N block has 2^N physically contiguous pages, with the BUDDY flag | |
| /// set for and _only_ for the first page. | |
| /// | |
| /// 15. COMPOUND_HEAD | |
| /// 16. COMPOUND_TAIL | |
| /// A compound page with order N consists of 2^N physically contiguous pages. | |
| /// A compound page with order 2 takes the form of "HTTT", where H donates its | |
| /// head page and T donates its tail page(s). The major consumers of compound | |
| /// pages are hugeTLB pages (Documentation/vm/hugetlbpage.txt), the SLUB etc. | |
| /// memory allocators and various device drivers. However in this interface, | |
| /// only huge/giga pages are made visible to end users. | |
| /// 17. HUGE | |
| /// this is an integral part of a HugeTLB page | |
| /// | |
| /// 19. HWPOISON | |
| /// hardware detected memory corruption on this page: don't touch the data! | |
| /// | |
| /// 20. NOPAGE | |
| /// no page frame exists at the requested address | |
| /// | |
| /// 21. KSM | |
| /// identical memory pages dynamically shared between one or more processes | |
| /// | |
| /// 22. THP | |
| /// contiguous pages which construct transparent hugepages | |
| /// | |
| /// 23. BALLOON | |
| /// balloon compaction page | |
| /// | |
| /// 24. ZERO_PAGE | |
| /// zero page for pfn_zero or huge_zero page | |
| /// | |
| /// 25. IDLE | |
| /// page has not been accessed since it was marked idle (see | |
| /// Documentation/vm/idle_page_tracking.txt). Note that this flag may be | |
| /// stale in case the page was accessed via a PTE. To make sure the flag | |
| /// is up-to-date one has to read /sys/kernel/mm/page_idle/bitmap first. | |
| /// | |
| /// [IO related page flags] | |
| /// 1. ERROR IO error occurred | |
| /// 3. UPTODATE page has up-to-date data | |
| /// ie. for file backed page: (in-memory data revision >= on-disk one) | |
| /// 4. DIRTY page has been written to, hence contains new data | |
| /// ie. for file backed page: (in-memory data revision > on-disk one) | |
| /// 8. WRITEBACK page is being synced to disk | |
| /// | |
| /// [LRU related page flags] | |
| /// 5. LRU page is in one of the LRU lists | |
| /// 6. ACTIVE page is in the active LRU list | |
| /// 18. UNEVICTABLE page is in the unevictable (non-)LRU list | |
| /// It is somehow pinned and not a candidate for LRU page reclaims, | |
| /// eg. ramfs pages, shmctl(SHM_LOCK) and mlock() memory segments | |
| /// 2. REFERENCED page has been referenced since last LRU list enqueue/requeue | |
| /// 9. RECLAIM page will be reclaimed soon after its pageout IO completed | |
| /// 11. MMAP a memory mapped page | |
| /// 12. ANON a memory mapped page that is not part of a file | |
| /// 13. SWAPCACHE page is mapped to swap space, ie. has an associated swap entry | |
| /// 14. SWAPBACKED page is backed by swap/RAM | |
| /// | |
| /// The page-types tool in the tools/vm directory can be used to query the | |
| /// above flags. | |
| /// | |
| /// Using pagemap to do something useful: | |
| /// | |
| /// The general procedure for using pagemap to find out about a process' memory | |
| /// usage goes like this: | |
| /// | |
| /// 1. Read /proc/pid/maps to determine which parts of the memory space are | |
| /// mapped to what. | |
| /// 2. Select the maps you are interested in -- all of them, or a particular | |
| /// library, or the stack or the heap, etc. | |
| /// 3. Open /proc/pid/pagemap and seek to the pages you would like to examine. | |
| /// 4. Read a u64 for each page from pagemap. | |
| /// 5. Open /proc/kpagecount and/or /proc/kpageflags. For each PFN you just | |
| /// read, seek to that entry in the file, and read the data you want. | |
| /// | |
| /// For example, to find the "unique set size" (USS), which is the amount of | |
| /// memory that a process is using that is not shared with any other process, | |
| /// you can go through every map in the process, find the PFNs, look those up | |
| /// in kpagecount, and tally up the number of pages that are only referenced | |
| /// once. | |
| /// | |
| /// Other notes: | |
| /// | |
| /// Reading from any of the files will return -EINVAL if you are not starting | |
| /// the read on an 8-byte boundary (e.g., if you sought an odd number of bytes | |
| /// into the file), or if the size of the read is not a multiple of 8 bytes. | |
| /// | |
| /// Before Linux 3.11 pagemap bits 55-60 were used for "page-shift" (which is | |
| /// always 12 at most architectures). Since Linux 3.11 their meaning changes | |
| /// after first clear of soft-dirty bits. Since Linux 4.2 they are used for | |
| /// flags unconditionally. | |
| /// ``` | |
| #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord, derive_more::Display, derive_more::From, derive_more::Into, derive_more::Binary, derive_more::LowerHex, derive_more::UpperHex, serde::Serialize, serde::Deserialize)] | |
| #[repr(transparent)] | |
| pub struct PageTableEntry(u64); | |
| impl PageTableEntry { | |
| const SOFT_DIRTY_BIT: u32 = 55; | |
| const PRESENT_BIT: u32 = 63; | |
| const PFN_BITS: u32= 55; | |
| pub const fn new(n: u64) -> Self { | |
| Self(n) | |
| } | |
| pub const fn page_frame_number(&self) -> u64 { | |
| const MASK: u64 = u64::max_value().wrapping_shr(u64::max_value().count_ones() - PageTableEntry::PFN_BITS); | |
| self.0 & MASK | |
| } | |
| pub const fn is_soft_dirty(&self) -> bool { | |
| const MASK: u64 = 1 << PageTableEntry::SOFT_DIRTY_BIT ; | |
| self.0 & MASK != 0 | |
| } | |
| pub const fn is_present(&self) -> bool { | |
| const MASK: u64 = 1 << PageTableEntry::PRESENT_BIT; | |
| self.0 & MASK != 0 | |
| } | |
| } | |
| impl<'a> TryFrom<&'a mut dyn Read> for PageTableEntry { | |
| type Error = Error; | |
| fn try_from(rdr: &'a mut dyn Read) -> Result<Self, Self::Error> { | |
| let mut buffer = [0u8; mem::size_of::<PageTableEntry>()]; | |
| rdr.read_exact(&mut buffer[..])?; | |
| Ok(PageTableEntry::new(u64::from_ne_bytes(buffer))) | |
| } | |
| } | |
| impl fmt::Debug for PageTableEntry { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| f.debug_struct("PageTableEntry") | |
| .field("value", &format!("{:b}", self)) | |
| .field("pfn",&self.page_frame_number()) | |
| .field("soft_dirty", &self.is_soft_dirty()) | |
| .field("present", &self.is_present()) | |
| .finish() | |
| } | |
| } | |
| #[derive(Debug, Clone)] | |
| pub struct ProcessVM { | |
| pid: usize, | |
| path: PathBuf, | |
| maps: Maps, | |
| } | |
| impl ProcessVM { | |
| pub fn with_pid(pid: usize) -> Result<Self, Error> { | |
| let path = proc_maps_path(pid); | |
| let mut reader = std::io::BufReader::new(std::fs::File::open(&path)?); | |
| let maps = Maps::try_from(&mut reader)?; | |
| Ok(Self { | |
| pid, | |
| path, | |
| maps, | |
| }) | |
| } | |
| pub fn maps(&self) -> &Maps { | |
| &self.maps | |
| } | |
| pub fn region(&self, addr: usize) -> Option<VMRegion<'_>> { | |
| self.maps.map.get(&addr).map(|region| { | |
| VMRegion{ | |
| pid: self.pid, | |
| region | |
| } | |
| }) | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord)] | |
| pub struct PageDescriptor { | |
| addr_range: AddressRange, | |
| pte: PageTableEntry, | |
| } | |
| pub struct VMRegionIter<R> { | |
| addr_range: AddressRange, | |
| page_size: usize, | |
| current_addr: usize, | |
| reader: R, | |
| } | |
| impl<R> Iterator for VMRegionIter<R> where R: Read { | |
| type Item = PageDescriptor; | |
| fn next(&mut self) -> Option<Self::Item> { | |
| if !self.addr_range.contains(self.current_addr) { | |
| return None; | |
| } | |
| let current_addr = self.current_addr; | |
| self.current_addr += self.page_size; | |
| let rdr: &mut dyn Read = &mut self.reader; | |
| PageTableEntry::try_from(rdr) | |
| .map_err(|err| { | |
| warn!("{:?}", err); | |
| err | |
| }) | |
| .ok() | |
| .map(|pte| { | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: current_addr, | |
| high: current_addr.checked_add(self.page_size).unwrap_or_else(|| panic!("out of range")), | |
| }, | |
| pte, | |
| } | |
| }) | |
| } | |
| } | |
| #[derive(Debug)] | |
| pub struct VMRegion<'a> { | |
| pid: usize, | |
| region: &'a Region, | |
| } | |
| impl<'a> VMRegion<'a> { | |
| const PAGESIZE: usize = 4096; | |
| pub fn try_iter(&self) -> Result<VMRegionIter<BufReader<std::fs::File>>, Error> { | |
| let path = proc_pagemaps_path(self.pid); | |
| let offset_entries = self.region.addr_range.low / VMRegion::PAGESIZE; | |
| let offset_bytes = offset_entries * mem::size_of::<PageTableEntry>(); | |
| let seek = std::io::SeekFrom::Start(u64::try_from(offset_bytes).unwrap_or_else(|err| panic!("{:?}", err))); | |
| let mut reader = BufReader::new(std::fs::File::open(&path)?); | |
| reader.seek(seek)?; | |
| Ok(VMRegionIter { | |
| addr_range: self.region.addr_range, | |
| page_size: 4096, | |
| current_addr: self.region.addr_range.low, | |
| reader | |
| }) | |
| } | |
| } | |
| #[test] | |
| fn test_parse() { | |
| const EXAMPLE_PROC_MAPS: &'static str = r#"00400000-004c0000 r-xp 00000000 103:01 270237 /usr/bin/zsh | |
| 006bf000-006c0000 r--p 000bf000 103:01 270237 /usr/bin/zsh | |
| 006c0000-006c7000 rw-p 000c0000 103:01 270237 /usr/bin/zsh | |
| 006c7000-006da000 rw-p 00000000 00:00 0 | |
| 00e08000-01135000 rw-p 00000000 00:00 0 [heap] | |
| 7fa281d2e000-7fa281d3e000 r-xp 00000000 103:01 270247 /usr/lib64/zsh/5.5.1/zsh/computil.so | |
| 7fa281d3e000-7fa281f3d000 ---p 00010000 103:01 270247 /usr/lib64/zsh/5.5.1/zsh/computil.so | |
| 7fa281f3d000-7fa281f3e000 r--p 0000f000 103:01 270247 /usr/lib64/zsh/5.5.1/zsh/computil.so | |
| 7fa281f3e000-7fa281f3f000 rw-p 00010000 103:01 270247 /usr/lib64/zsh/5.5.1/zsh/computil.so | |
| 7fa281f3f000-7fa281f42000 r-xp 00000000 103:01 270269 /usr/lib64/zsh/5.5.1/zsh/stat.so | |
| 7fa281f42000-7fa282141000 ---p 00003000 103:01 270269 /usr/lib64/zsh/5.5.1/zsh/stat.so | |
| 7fa282141000-7fa282142000 r--p 00002000 103:01 270269 /usr/lib64/zsh/5.5.1/zsh/stat.so | |
| 7fa282142000-7fa282143000 rw-p 00003000 103:01 270269 /usr/lib64/zsh/5.5.1/zsh/stat.so | |
| 7fa282143000-7fa282145000 r-xp 00000000 103:01 270272 /usr/lib64/zsh/5.5.1/zsh/terminfo.so | |
| 7fa282145000-7fa282344000 ---p 00002000 103:01 270272 /usr/lib64/zsh/5.5.1/zsh/terminfo.so | |
| 7fa282344000-7fa282345000 r--p 00001000 103:01 270272 /usr/lib64/zsh/5.5.1/zsh/terminfo.so | |
| 7fa282345000-7fa282346000 rw-p 00002000 103:01 270272 /usr/lib64/zsh/5.5.1/zsh/terminfo.so | |
| 7fa282346000-7fa282348000 r-xp 00000000 103:01 270255 /usr/lib64/zsh/5.5.1/zsh/langinfo.so | |
| 7fa282348000-7fa282547000 ---p 00002000 103:01 270255 /usr/lib64/zsh/5.5.1/zsh/langinfo.so | |
| 7fa282547000-7fa282548000 r--p 00001000 103:01 270255 /usr/lib64/zsh/5.5.1/zsh/langinfo.so | |
| 7fa282548000-7fa282549000 rw-p 00002000 103:01 270255 /usr/lib64/zsh/5.5.1/zsh/langinfo.so | |
| 7fa282549000-7fa282557000 r-xp 00000000 103:01 270246 /usr/lib64/zsh/5.5.1/zsh/complist.so | |
| 7fa282557000-7fa282757000 ---p 0000e000 103:01 270246 /usr/lib64/zsh/5.5.1/zsh/complist.so | |
| 7fa282757000-7fa282758000 r--p 0000e000 103:01 270246 /usr/lib64/zsh/5.5.1/zsh/complist.so | |
| 7fa282758000-7fa282759000 rw-p 0000f000 103:01 270246 /usr/lib64/zsh/5.5.1/zsh/complist.so | |
| 7fa282759000-7fa282761000 r-xp 00000000 103:01 270279 /usr/lib64/zsh/5.5.1/zsh/zutil.so | |
| 7fa282761000-7fa282960000 ---p 00008000 103:01 270279 /usr/lib64/zsh/5.5.1/zsh/zutil.so | |
| 7fa282960000-7fa282961000 r--p 00007000 103:01 270279 /usr/lib64/zsh/5.5.1/zsh/zutil.so | |
| 7fa282961000-7fa282962000 rw-p 00008000 103:01 270279 /usr/lib64/zsh/5.5.1/zsh/zutil.so | |
| 7fa282962000-7fa282985000 r-xp 00000000 103:01 270245 /usr/lib64/zsh/5.5.1/zsh/complete.so | |
| 7fa282985000-7fa282b85000 ---p 00023000 103:01 270245 /usr/lib64/zsh/5.5.1/zsh/complete.so | |
| 7fa282b85000-7fa282b86000 r--p 00023000 103:01 270245 /usr/lib64/zsh/5.5.1/zsh/complete.so | |
| 7fa282b86000-7fa282b87000 rw-p 00024000 103:01 270245 /usr/lib64/zsh/5.5.1/zsh/complete.so | |
| 7fa282b87000-7fa282b88000 rw-p 00000000 00:00 0 | |
| 7fa282b88000-7fa282b92000 r-xp 00000000 103:01 270264 /usr/lib64/zsh/5.5.1/zsh/parameter.so | |
| 7fa282b92000-7fa282d91000 ---p 0000a000 103:01 270264 /usr/lib64/zsh/5.5.1/zsh/parameter.so | |
| 7fa282d91000-7fa282d92000 r--p 00009000 103:01 270264 /usr/lib64/zsh/5.5.1/zsh/parameter.so | |
| 7fa282d92000-7fa282d93000 rw-p 0000a000 103:01 270264 /usr/lib64/zsh/5.5.1/zsh/parameter.so | |
| 7fa282d93000-7fa282ddb000 r-xp 00000000 103:01 270274 /usr/lib64/zsh/5.5.1/zsh/zle.so | |
| 7fa282ddb000-7fa282fda000 ---p 00048000 103:01 270274 /usr/lib64/zsh/5.5.1/zsh/zle.so | |
| 7fa282fda000-7fa282fdc000 r--p 00047000 103:01 270274 /usr/lib64/zsh/5.5.1/zsh/zle.so | |
| 7fa282fdc000-7fa282fe4000 rw-p 00049000 103:01 270274 /usr/lib64/zsh/5.5.1/zsh/zle.so | |
| 7fa282fe4000-7fa289bb3000 r--p 00000000 103:01 276804 /usr/lib/locale/locale-archive | |
| 7fa289bb3000-7fa289bcb000 r-xp 00000000 103:01 282043 /usr/lib64/libpthread-2.26.so | |
| 7fa289bcb000-7fa289dcb000 ---p 00018000 103:01 282043 /usr/lib64/libpthread-2.26.so | |
| 7fa289dcb000-7fa289dcc000 r--p 00018000 103:01 282043 /usr/lib64/libpthread-2.26.so | |
| 7fa289dcc000-7fa289dcd000 rw-p 00019000 103:01 282043 /usr/lib64/libpthread-2.26.so | |
| 7fa289dcd000-7fa289dd1000 rw-p 00000000 00:00 0 | |
| 7fa289dd1000-7fa289f72000 r-xp 00000000 103:01 264810 /usr/lib64/libc-2.26.so | |
| 7fa289f72000-7fa28a172000 ---p 001a1000 103:01 264810 /usr/lib64/libc-2.26.so | |
| 7fa28a172000-7fa28a176000 r--p 001a1000 103:01 264810 /usr/lib64/libc-2.26.so | |
| 7fa28a176000-7fa28a178000 rw-p 001a5000 103:01 264810 /usr/lib64/libc-2.26.so | |
| 7fa28a178000-7fa28a17c000 rw-p 00000000 00:00 0 | |
| 7fa28a17c000-7fa28a2bb000 r-xp 00000000 103:01 264817 /usr/lib64/libm-2.26.so | |
| 7fa28a2bb000-7fa28a4ba000 ---p 0013f000 103:01 264817 /usr/lib64/libm-2.26.so | |
| 7fa28a4ba000-7fa28a4bb000 r--p 0013e000 103:01 264817 /usr/lib64/libm-2.26.so | |
| 7fa28a4bb000-7fa28a4bc000 rw-p 0013f000 103:01 264817 /usr/lib64/libm-2.26.so | |
| 7fa28a4bc000-7fa28a4c3000 r-xp 00000000 103:01 289012 /usr/lib64/librt-2.26.so | |
| 7fa28a4c3000-7fa28a6c2000 ---p 00007000 103:01 289012 /usr/lib64/librt-2.26.so | |
| 7fa28a6c2000-7fa28a6c3000 r--p 00006000 103:01 289012 /usr/lib64/librt-2.26.so | |
| 7fa28a6c3000-7fa28a6c4000 rw-p 00007000 103:01 289012 /usr/lib64/librt-2.26.so | |
| 7fa28a6c4000-7fa28a6eb000 r-xp 00000000 103:01 265142 /usr/lib64/libtinfo.so.6.0 | |
| 7fa28a6eb000-7fa28a8ea000 ---p 00027000 103:01 265142 /usr/lib64/libtinfo.so.6.0 | |
| 7fa28a8ea000-7fa28a8ee000 r--p 00026000 103:01 265142 /usr/lib64/libtinfo.so.6.0 | |
| 7fa28a8ee000-7fa28a8ef000 rw-p 0002a000 103:01 265142 /usr/lib64/libtinfo.so.6.0 | |
| 7fa28a8ef000-7fa28a924000 r-xp 00000000 103:01 265134 /usr/lib64/libncursesw.so.6.0 | |
| 7fa28a924000-7fa28ab24000 ---p 00035000 103:01 265134 /usr/lib64/libncursesw.so.6.0 | |
| 7fa28ab24000-7fa28ab25000 r--p 00035000 103:01 265134 /usr/lib64/libncursesw.so.6.0 | |
| 7fa28ab25000-7fa28ab26000 rw-p 00036000 103:01 265134 /usr/lib64/libncursesw.so.6.0 | |
| 7fa28ab26000-7fa28ab29000 r-xp 00000000 103:01 264815 /usr/lib64/libdl-2.26.so | |
| 7fa28ab29000-7fa28ad28000 ---p 00003000 103:01 264815 /usr/lib64/libdl-2.26.so | |
| 7fa28ad28000-7fa28ad29000 r--p 00002000 103:01 264815 /usr/lib64/libdl-2.26.so | |
| 7fa28ad29000-7fa28ad2a000 rw-p 00003000 103:01 264815 /usr/lib64/libdl-2.26.so | |
| 7fa28ad2a000-7fa28ad8d000 r-xp 00000000 103:01 265311 /usr/lib64/libpcre.so.1.2.0 | |
| 7fa28ad8d000-7fa28af8c000 ---p 00063000 103:01 265311 /usr/lib64/libpcre.so.1.2.0 | |
| 7fa28af8c000-7fa28af8d000 r--p 00062000 103:01 265311 /usr/lib64/libpcre.so.1.2.0 | |
| 7fa28af8d000-7fa28af8e000 rw-p 00063000 103:01 265311 /usr/lib64/libpcre.so.1.2.0 | |
| 7fa28af8e000-7fa28af9a000 r-xp 00000000 103:01 266388 /usr/lib64/libgdbm.so.4.0.0 | |
| 7fa28af9a000-7fa28b199000 ---p 0000c000 103:01 266388 /usr/lib64/libgdbm.so.4.0.0 | |
| 7fa28b199000-7fa28b19a000 r--p 0000b000 103:01 266388 /usr/lib64/libgdbm.so.4.0.0 | |
| 7fa28b19a000-7fa28b19b000 rw-p 0000c000 103:01 266388 /usr/lib64/libgdbm.so.4.0.0 | |
| 7fa28b19b000-7fa28b1bf000 r-xp 00000000 103:01 264698 /usr/lib64/ld-2.26.so | |
| 7fa28b36e000-7fa28b3a3000 r--s 00000000 103:01 132098 /var/db/nscd/passwd | |
| 7fa28b3a3000-7fa28b3a9000 rw-p 00000000 00:00 0 | |
| 7fa28b3af000-7fa28b3b6000 r--s 00000000 103:01 265116 /usr/lib64/gconv/gconv-modules.cache | |
| 7fa28b3ba000-7fa28b3be000 rw-p 00000000 00:00 0 | |
| 7fa28b3be000-7fa28b3bf000 r--p 00023000 103:01 264698 /usr/lib64/ld-2.26.so | |
| 7fa28b3bf000-7fa28b3c0000 rw-p 00024000 103:01 264698 /usr/lib64/ld-2.26.so | |
| 7fa28b3c0000-7fa28b3c1000 rw-p 00000000 00:00 0 | |
| 7ffce82d7000-7ffce831f000 rw-p 00000000 00:00 0 [stack] | |
| 7ffce83c1000-7ffce83c4000 r--p 00000000 00:00 0 [vvar] | |
| 7ffce83c4000-7ffce83c6000 r-xp 00000000 00:00 0 [vdso] | |
| ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] | |
| "#; | |
| let pagemap = Maps::try_from(EXAMPLE_PROC_MAPS).unwrap(); | |
| eprintln!("{:#?}", pagemap); | |
| println!("{}", pagemap); | |
| assert_eq!(pagemap.map.len(), EXAMPLE_PROC_MAPS.lines().count()); | |
| assert_eq!(&format!("{}", pagemap), EXAMPLE_PROC_MAPS); | |
| } | |
| #[derive(Debug, StructOpt)] | |
| #[structopt(name = "beholder", about = "pagemap parsing")] | |
| struct Opt { | |
| /// Activate debug mode | |
| #[structopt(short, long)] | |
| pid: usize, | |
| #[structopt(short, long)] | |
| json: bool, | |
| #[structopt(short, long)] | |
| region: String, | |
| } | |
| fn run() -> Result<(), Box<dyn std::error::Error>> { | |
| let args = Opt::from_args(); | |
| let path = Path::new("/") | |
| .join("proc") | |
| .join(&args.pid.to_string()) | |
| .join("maps"); | |
| eprintln!("parsing: {}", path.to_str().unwrap()); | |
| let mut vm = ProcessVM::with_pid(args.pid)?; | |
| println!("{}", vm.maps()); | |
| let addr = usize::from_str_radix(&args.region, 16)?; | |
| let region = vm.region(addr); | |
| println!("{:#?}", region); | |
| for p in region.unwrap().try_iter()? { | |
| println!("{:#?}", p); | |
| } | |
| Ok(()) | |
| } | |
| fn main() { | |
| run().unwrap() | |
| } | |
| /* | |
| $ cargo run -- --pid $$ --region 7ffce82d7000 | |
| parsing: /proc/62800/maps | |
| 00400000-004c0000 r-xp 00000000 103:01 270237 /usr/bin/zsh | |
| 006bf000-006c0000 r--p 000bf000 103:01 270237 /usr/bin/zsh | |
| 006c0000-006c7000 rw-p 000c0000 103:01 270237 /usr/bin/zsh | |
| 006c7000-006da000 rw-p 00000000 00:00 0 | |
| 00e08000-01175000 rw-p 00000000 00:00 0 [heap] | |
| 7fa281d2e000-7fa281d3e000 r-xp 00000000 103:01 270247 /usr/lib64/zsh/5.5.1/zsh/computil.so | |
| 7fa281d3e000-7fa281f3d000 ---p 00010000 103:01 270247 /usr/lib64/zsh/5.5.1/zsh/computil.so | |
| 7fa281f3d000-7fa281f3e000 r--p 0000f000 103:01 270247 /usr/lib64/zsh/5.5.1/zsh/computil.so | |
| 7fa281f3e000-7fa281f3f000 rw-p 00010000 103:01 270247 /usr/lib64/zsh/5.5.1/zsh/computil.so | |
| 7fa281f3f000-7fa281f42000 r-xp 00000000 103:01 270269 /usr/lib64/zsh/5.5.1/zsh/stat.so | |
| 7fa281f42000-7fa282141000 ---p 00003000 103:01 270269 /usr/lib64/zsh/5.5.1/zsh/stat.so | |
| 7fa282141000-7fa282142000 r--p 00002000 103:01 270269 /usr/lib64/zsh/5.5.1/zsh/stat.so | |
| 7fa282142000-7fa282143000 rw-p 00003000 103:01 270269 /usr/lib64/zsh/5.5.1/zsh/stat.so | |
| 7fa282143000-7fa282145000 r-xp 00000000 103:01 270272 /usr/lib64/zsh/5.5.1/zsh/terminfo.so | |
| 7fa282145000-7fa282344000 ---p 00002000 103:01 270272 /usr/lib64/zsh/5.5.1/zsh/terminfo.so | |
| 7fa282344000-7fa282345000 r--p 00001000 103:01 270272 /usr/lib64/zsh/5.5.1/zsh/terminfo.so | |
| 7fa282345000-7fa282346000 rw-p 00002000 103:01 270272 /usr/lib64/zsh/5.5.1/zsh/terminfo.so | |
| 7fa282346000-7fa282348000 r-xp 00000000 103:01 270255 /usr/lib64/zsh/5.5.1/zsh/langinfo.so | |
| 7fa282348000-7fa282547000 ---p 00002000 103:01 270255 /usr/lib64/zsh/5.5.1/zsh/langinfo.so | |
| 7fa282547000-7fa282548000 r--p 00001000 103:01 270255 /usr/lib64/zsh/5.5.1/zsh/langinfo.so | |
| 7fa282548000-7fa282549000 rw-p 00002000 103:01 270255 /usr/lib64/zsh/5.5.1/zsh/langinfo.so | |
| 7fa282549000-7fa282557000 r-xp 00000000 103:01 270246 /usr/lib64/zsh/5.5.1/zsh/complist.so | |
| 7fa282557000-7fa282757000 ---p 0000e000 103:01 270246 /usr/lib64/zsh/5.5.1/zsh/complist.so | |
| 7fa282757000-7fa282758000 r--p 0000e000 103:01 270246 /usr/lib64/zsh/5.5.1/zsh/complist.so | |
| 7fa282758000-7fa282759000 rw-p 0000f000 103:01 270246 /usr/lib64/zsh/5.5.1/zsh/complist.so | |
| 7fa282759000-7fa282761000 r-xp 00000000 103:01 270279 /usr/lib64/zsh/5.5.1/zsh/zutil.so | |
| 7fa282761000-7fa282960000 ---p 00008000 103:01 270279 /usr/lib64/zsh/5.5.1/zsh/zutil.so | |
| 7fa282960000-7fa282961000 r--p 00007000 103:01 270279 /usr/lib64/zsh/5.5.1/zsh/zutil.so | |
| 7fa282961000-7fa282962000 rw-p 00008000 103:01 270279 /usr/lib64/zsh/5.5.1/zsh/zutil.so | |
| 7fa282962000-7fa282985000 r-xp 00000000 103:01 270245 /usr/lib64/zsh/5.5.1/zsh/complete.so | |
| 7fa282985000-7fa282b85000 ---p 00023000 103:01 270245 /usr/lib64/zsh/5.5.1/zsh/complete.so | |
| 7fa282b85000-7fa282b86000 r--p 00023000 103:01 270245 /usr/lib64/zsh/5.5.1/zsh/complete.so | |
| 7fa282b86000-7fa282b87000 rw-p 00024000 103:01 270245 /usr/lib64/zsh/5.5.1/zsh/complete.so | |
| 7fa282b87000-7fa282b88000 rw-p 00000000 00:00 0 | |
| 7fa282b88000-7fa282b92000 r-xp 00000000 103:01 270264 /usr/lib64/zsh/5.5.1/zsh/parameter.so | |
| 7fa282b92000-7fa282d91000 ---p 0000a000 103:01 270264 /usr/lib64/zsh/5.5.1/zsh/parameter.so | |
| 7fa282d91000-7fa282d92000 r--p 00009000 103:01 270264 /usr/lib64/zsh/5.5.1/zsh/parameter.so | |
| 7fa282d92000-7fa282d93000 rw-p 0000a000 103:01 270264 /usr/lib64/zsh/5.5.1/zsh/parameter.so | |
| 7fa282d93000-7fa282ddb000 r-xp 00000000 103:01 270274 /usr/lib64/zsh/5.5.1/zsh/zle.so | |
| 7fa282ddb000-7fa282fda000 ---p 00048000 103:01 270274 /usr/lib64/zsh/5.5.1/zsh/zle.so | |
| 7fa282fda000-7fa282fdc000 r--p 00047000 103:01 270274 /usr/lib64/zsh/5.5.1/zsh/zle.so | |
| 7fa282fdc000-7fa282fe4000 rw-p 00049000 103:01 270274 /usr/lib64/zsh/5.5.1/zsh/zle.so | |
| 7fa282fe4000-7fa289bb3000 r--p 00000000 103:01 276804 /usr/lib/locale/locale-archive | |
| 7fa289bb3000-7fa289bcb000 r-xp 00000000 103:01 282043 /usr/lib64/libpthread-2.26.so | |
| 7fa289bcb000-7fa289dcb000 ---p 00018000 103:01 282043 /usr/lib64/libpthread-2.26.so | |
| 7fa289dcb000-7fa289dcc000 r--p 00018000 103:01 282043 /usr/lib64/libpthread-2.26.so | |
| 7fa289dcc000-7fa289dcd000 rw-p 00019000 103:01 282043 /usr/lib64/libpthread-2.26.so | |
| 7fa289dcd000-7fa289dd1000 rw-p 00000000 00:00 0 | |
| 7fa289dd1000-7fa289f72000 r-xp 00000000 103:01 264810 /usr/lib64/libc-2.26.so | |
| 7fa289f72000-7fa28a172000 ---p 001a1000 103:01 264810 /usr/lib64/libc-2.26.so | |
| 7fa28a172000-7fa28a176000 r--p 001a1000 103:01 264810 /usr/lib64/libc-2.26.so | |
| 7fa28a176000-7fa28a178000 rw-p 001a5000 103:01 264810 /usr/lib64/libc-2.26.so | |
| 7fa28a178000-7fa28a17c000 rw-p 00000000 00:00 0 | |
| 7fa28a17c000-7fa28a2bb000 r-xp 00000000 103:01 264817 /usr/lib64/libm-2.26.so | |
| 7fa28a2bb000-7fa28a4ba000 ---p 0013f000 103:01 264817 /usr/lib64/libm-2.26.so | |
| 7fa28a4ba000-7fa28a4bb000 r--p 0013e000 103:01 264817 /usr/lib64/libm-2.26.so | |
| 7fa28a4bb000-7fa28a4bc000 rw-p 0013f000 103:01 264817 /usr/lib64/libm-2.26.so | |
| 7fa28a4bc000-7fa28a4c3000 r-xp 00000000 103:01 289012 /usr/lib64/librt-2.26.so | |
| 7fa28a4c3000-7fa28a6c2000 ---p 00007000 103:01 289012 /usr/lib64/librt-2.26.so | |
| 7fa28a6c2000-7fa28a6c3000 r--p 00006000 103:01 289012 /usr/lib64/librt-2.26.so | |
| 7fa28a6c3000-7fa28a6c4000 rw-p 00007000 103:01 289012 /usr/lib64/librt-2.26.so | |
| 7fa28a6c4000-7fa28a6eb000 r-xp 00000000 103:01 265142 /usr/lib64/libtinfo.so.6.0 | |
| 7fa28a6eb000-7fa28a8ea000 ---p 00027000 103:01 265142 /usr/lib64/libtinfo.so.6.0 | |
| 7fa28a8ea000-7fa28a8ee000 r--p 00026000 103:01 265142 /usr/lib64/libtinfo.so.6.0 | |
| 7fa28a8ee000-7fa28a8ef000 rw-p 0002a000 103:01 265142 /usr/lib64/libtinfo.so.6.0 | |
| 7fa28a8ef000-7fa28a924000 r-xp 00000000 103:01 265134 /usr/lib64/libncursesw.so.6.0 | |
| 7fa28a924000-7fa28ab24000 ---p 00035000 103:01 265134 /usr/lib64/libncursesw.so.6.0 | |
| 7fa28ab24000-7fa28ab25000 r--p 00035000 103:01 265134 /usr/lib64/libncursesw.so.6.0 | |
| 7fa28ab25000-7fa28ab26000 rw-p 00036000 103:01 265134 /usr/lib64/libncursesw.so.6.0 | |
| 7fa28ab26000-7fa28ab29000 r-xp 00000000 103:01 264815 /usr/lib64/libdl-2.26.so | |
| 7fa28ab29000-7fa28ad28000 ---p 00003000 103:01 264815 /usr/lib64/libdl-2.26.so | |
| 7fa28ad28000-7fa28ad29000 r--p 00002000 103:01 264815 /usr/lib64/libdl-2.26.so | |
| 7fa28ad29000-7fa28ad2a000 rw-p 00003000 103:01 264815 /usr/lib64/libdl-2.26.so | |
| 7fa28ad2a000-7fa28ad8d000 r-xp 00000000 103:01 265311 /usr/lib64/libpcre.so.1.2.0 | |
| 7fa28ad8d000-7fa28af8c000 ---p 00063000 103:01 265311 /usr/lib64/libpcre.so.1.2.0 | |
| 7fa28af8c000-7fa28af8d000 r--p 00062000 103:01 265311 /usr/lib64/libpcre.so.1.2.0 | |
| 7fa28af8d000-7fa28af8e000 rw-p 00063000 103:01 265311 /usr/lib64/libpcre.so.1.2.0 | |
| 7fa28af8e000-7fa28af9a000 r-xp 00000000 103:01 266388 /usr/lib64/libgdbm.so.4.0.0 | |
| 7fa28af9a000-7fa28b199000 ---p 0000c000 103:01 266388 /usr/lib64/libgdbm.so.4.0.0 | |
| 7fa28b199000-7fa28b19a000 r--p 0000b000 103:01 266388 /usr/lib64/libgdbm.so.4.0.0 | |
| 7fa28b19a000-7fa28b19b000 rw-p 0000c000 103:01 266388 /usr/lib64/libgdbm.so.4.0.0 | |
| 7fa28b19b000-7fa28b1bf000 r-xp 00000000 103:01 264698 /usr/lib64/ld-2.26.so | |
| 7fa28b36e000-7fa28b3a3000 r--s 00000000 103:01 132098 /var/db/nscd/passwd | |
| 7fa28b3a3000-7fa28b3a9000 rw-p 00000000 00:00 0 | |
| 7fa28b3af000-7fa28b3b6000 r--s 00000000 103:01 265116 /usr/lib64/gconv/gconv-modules.cache | |
| 7fa28b3ba000-7fa28b3be000 rw-p 00000000 00:00 0 | |
| 7fa28b3be000-7fa28b3bf000 r--p 00023000 103:01 264698 /usr/lib64/ld-2.26.so | |
| 7fa28b3bf000-7fa28b3c0000 rw-p 00024000 103:01 264698 /usr/lib64/ld-2.26.so | |
| 7fa28b3c0000-7fa28b3c1000 rw-p 00000000 00:00 0 | |
| 7ffce82d7000-7ffce831f000 rw-p 00000000 00:00 0 [stack] | |
| 7ffce83c1000-7ffce83c4000 r--p 00000000 00:00 0 [vvar] | |
| 7ffce83c4000-7ffce83c6000 r-xp 00000000 00:00 0 [vdso] | |
| ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] | |
| Some( | |
| VMRegion { | |
| pid: 62800, | |
| region: Region { | |
| addr_range: AddressRange { | |
| low: 140724203778048, | |
| high: 140724204072960, | |
| }, | |
| perms: PermSet( | |
| [ | |
| Read, | |
| Write, | |
| Nil, | |
| Private, | |
| ], | |
| ), | |
| offset: Offset( | |
| 0, | |
| ), | |
| device: Device { | |
| major: 0, | |
| minor: 0, | |
| }, | |
| inode: Inode( | |
| 0, | |
| ), | |
| pathname: Pseudo( | |
| "[stack]", | |
| ), | |
| extra: [], | |
| }, | |
| }, | |
| ) | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203778048, | |
| high: 140724203782144, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000000110101111111110011001110", | |
| pfn: 14154958, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203782144, | |
| high: 140724203786240, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000000111110110101101011101111", | |
| pfn: 16472815, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203786240, | |
| high: 140724203790336, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000000110101111110110011010111", | |
| pfn: 14150871, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203790336, | |
| high: 140724203794432, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000001000000011010001000010010", | |
| pfn: 16884242, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203794432, | |
| high: 140724203798528, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000000111111010010101110011001", | |
| pfn: 16591769, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203798528, | |
| high: 140724203802624, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000001000000000100010001101101", | |
| pfn: 16794733, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203802624, | |
| high: 140724203806720, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000000111100101111011100110011", | |
| pfn: 15922995, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203806720, | |
| high: 140724203810816, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000001000001111100110001110110", | |
| pfn: 17288310, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203810816, | |
| high: 140724203814912, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000000110110111001100011100100", | |
| pfn: 14391524, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203814912, | |
| high: 140724203819008, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000001000010101110111001010100", | |
| pfn: 17493588, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203819008, | |
| high: 140724203823104, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000000111111110111101110000100", | |
| pfn: 16743300, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203823104, | |
| high: 140724203827200, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000000010010111011010100110100", | |
| pfn: 4961588, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203827200, | |
| high: 140724203831296, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001000000101001111011110000", | |
| pfn: 16948976, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203831296, | |
| high: 140724203835392, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111111010000000001011010", | |
| pfn: 16580698, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203835392, | |
| high: 140724203839488, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111110110000011110110000", | |
| pfn: 16451504, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203839488, | |
| high: 140724203843584, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111001111000010001010000", | |
| pfn: 15172688, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203843584, | |
| high: 140724203847680, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000101101110000001011111100", | |
| pfn: 11993852, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203847680, | |
| high: 140724203851776, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000101110001010001110011111", | |
| pfn: 12100511, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203851776, | |
| high: 140724203855872, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001000001110011000111101100", | |
| pfn: 17248748, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203855872, | |
| high: 140724203859968, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111100011010000111011001", | |
| pfn: 15835609, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203859968, | |
| high: 140724203864064, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111111001011111011010111", | |
| pfn: 16563927, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203864064, | |
| high: 140724203868160, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000110111110110101011101000", | |
| pfn: 14641896, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203868160, | |
| high: 140724203872256, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000000101000001010100000001", | |
| pfn: 1316097, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203872256, | |
| high: 140724203876352, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000110110000111101000000000", | |
| pfn: 14187008, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203876352, | |
| high: 140724203880448, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111010001110100011100000", | |
| pfn: 15263968, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203880448, | |
| high: 140724203884544, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001000001001010110111010101", | |
| pfn: 17083861, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203884544, | |
| high: 140724203888640, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111101110110110011000111", | |
| pfn: 16215239, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203888640, | |
| high: 140724203892736, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001000001010001010100100011", | |
| pfn: 17110307, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203892736, | |
| high: 140724203896832, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000000101011010110011101011", | |
| pfn: 1420523, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203896832, | |
| high: 140724203900928, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111110100101110011111011", | |
| pfn: 16407803, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203900928, | |
| high: 140724203905024, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111001100111011101100001", | |
| pfn: 15103841, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203905024, | |
| high: 140724203909120, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001000011000101011100110101", | |
| pfn: 17585973, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203909120, | |
| high: 140724203913216, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000101110010011010101011101", | |
| pfn: 12137821, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203913216, | |
| high: 140724203917312, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111001010010101111111001", | |
| pfn: 15019001, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203917312, | |
| high: 140724203921408, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000110110111101011000100101", | |
| pfn: 14407205, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203921408, | |
| high: 140724203925504, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000101001001001000000010000", | |
| pfn: 10784784, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203925504, | |
| high: 140724203929600, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000110100111000101000001111", | |
| pfn: 13863439, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203929600, | |
| high: 140724203933696, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000101110001001011010000101", | |
| pfn: 12097157, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203933696, | |
| high: 140724203937792, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111101111101001100111000", | |
| pfn: 16241464, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203937792, | |
| high: 140724203941888, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001000000111011010000111111", | |
| pfn: 17019967, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203941888, | |
| high: 140724203945984, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111001000100110101001000", | |
| pfn: 14961992, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203945984, | |
| high: 140724203950080, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111100111101101101100001", | |
| pfn: 15981409, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203950080, | |
| high: 140724203954176, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111001000100101101001000", | |
| pfn: 14961480, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203954176, | |
| high: 140724203958272, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111011011000010110001101", | |
| pfn: 15566221, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203958272, | |
| high: 140724203962368, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000110100111001001000100011", | |
| pfn: 13865507, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203962368, | |
| high: 140724203966464, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000110110001010011011101000", | |
| pfn: 14198504, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203966464, | |
| high: 140724203970560, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111011101010111100101111", | |
| pfn: 15642415, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203970560, | |
| high: 140724203974656, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111011011100001001011001", | |
| pfn: 15581785, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203974656, | |
| high: 140724203978752, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010000111111000011100011011", | |
| pfn: 35620635, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203978752, | |
| high: 140724203982848, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010001100111001100001001100", | |
| pfn: 36935756, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203982848, | |
| high: 140724203986944, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010001001010100010111001110", | |
| pfn: 35997134, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203986944, | |
| high: 140724203991040, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010000111111111001110011100", | |
| pfn: 35648412, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203991040, | |
| high: 140724203995136, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010001011100001111011010011", | |
| pfn: 36576979, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203995136, | |
| high: 140724203999232, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001010111011010010110000000", | |
| pfn: 22914432, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724203999232, | |
| high: 140724204003328, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111100000111001110010100", | |
| pfn: 15758228, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204003328, | |
| high: 140724204007424, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010000110000110101011100101", | |
| pfn: 35154661, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204007424, | |
| high: 140724204011520, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010001100100010010101011000", | |
| pfn: 36840792, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204011520, | |
| high: 140724204015616, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001000000011011100110111001", | |
| pfn: 16890297, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204015616, | |
| high: 140724204019712, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010001010011000100011111001", | |
| pfn: 36276473, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204019712, | |
| high: 140724204023808, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001111110110101010101001111", | |
| pfn: 33248591, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204023808, | |
| high: 140724204027904, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111111111010001011110100", | |
| pfn: 16753396, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204027904, | |
| high: 140724204032000, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111001011001001110100100", | |
| pfn: 15045540, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204032000, | |
| high: 140724204036096, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010000011001001001011110100", | |
| pfn: 34378484, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204036096, | |
| high: 140724204040192, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000001000100010000100010100010", | |
| pfn: 17893538, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204040192, | |
| high: 140724204044288, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010001001000111100000110000", | |
| pfn: 35944496, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204044288, | |
| high: 140724204048384, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000101110001010010011110000", | |
| pfn: 12100848, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204048384, | |
| high: 140724204052480, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010000101110001100001111110", | |
| pfn: 35068030, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204052480, | |
| high: 140724204056576, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000010000101111001110111010001", | |
| pfn: 35102161, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204056576, | |
| high: 140724204060672, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111101010010011011000110", | |
| pfn: 16066246, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204060672, | |
| high: 140724204064768, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000111000011101110000101110", | |
| pfn: 14801966, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204064768, | |
| high: 140724204068864, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000110000000000000000000000000000000110101111011001010111111", | |
| pfn: 14135999, | |
| soft_dirty: true, | |
| present: true, | |
| }, | |
| } | |
| PageDescriptor { | |
| addr_range: AddressRange { | |
| low: 140724204068864, | |
| high: 140724204072960, | |
| }, | |
| pte: PageTableEntry { | |
| value: "1000000100000000000000000000000000000001000001010011001111101011", | |
| pfn: 17118187, | |
| soft_dirty: false, | |
| present: true, | |
| }, | |
| } | |
| */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment