Skip to content

Instantly share code, notes, and snippets.

@dillonhicks
Created July 23, 2020 01:29
Show Gist options
  • Select an option

  • Save dillonhicks/213fc66c64b8cc6b6831bfb81e03d0ed to your computer and use it in GitHub Desktop.

Select an option

Save dillonhicks/213fc66c64b8cc6b6831bfb81e03d0ed to your computer and use it in GitHub Desktop.
Parsing Linux Process PageMaps in Rust
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