procfs_core/process/
mod.rs

1//! Functions and structs related to process information
2//!
3//! The primary source of data for functions in this module is the files in a `/proc/<pid>/`
4//! directory.
5
6use super::*;
7use crate::from_iter;
8
9#[cfg(feature = "serde1")]
10use serde::{Deserialize, Serialize};
11use std::io::Read;
12use std::path::PathBuf;
13use std::str::FromStr;
14
15mod limit;
16pub use limit::*;
17
18mod stat;
19pub use stat::*;
20
21mod mount;
22pub use mount::*;
23
24mod namespaces;
25pub use namespaces::*;
26
27mod status;
28pub use status::*;
29
30mod schedstat;
31pub use schedstat::*;
32
33mod smaps_rollup;
34pub use smaps_rollup::*;
35
36mod pagemap;
37pub use pagemap::*;
38
39mod clear_refs;
40pub use clear_refs::*;
41
42bitflags! {
43    /// Kernel flags for a process
44    ///
45    /// See also the [Stat::flags()] method.
46    #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
47    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
48    pub struct StatFlags: u32 {
49        /// I am an IDLE thread
50        const PF_IDLE = 0x0000_0002;
51        /// Getting shut down
52        const PF_EXITING = 0x0000_0004;
53        /// PI exit done on shut down
54        const PF_EXITPIDONE = 0x0000_0008;
55        /// I'm a virtual CPU
56        const PF_VCPU = 0x0000_0010;
57        /// I'm a workqueue worker
58        const PF_WQ_WORKER = 0x0000_0020;
59        /// Forked but didn't exec
60        const PF_FORKNOEXEC = 0x0000_0040;
61        /// Process policy on mce errors;
62        const PF_MCE_PROCESS = 0x0000_0080;
63        /// Used super-user privileges
64        const PF_SUPERPRIV = 0x0000_0100;
65        /// Dumped core
66        const PF_DUMPCORE = 0x0000_0200;
67        /// Killed by a signal
68        const PF_SIGNALED = 0x0000_0400;
69        ///Allocating memory
70        const PF_MEMALLOC = 0x0000_0800;
71        /// set_user() noticed that RLIMIT_NPROC was exceeded
72        const PF_NPROC_EXCEEDED = 0x0000_1000;
73        /// If unset the fpu must be initialized before use
74        const PF_USED_MATH = 0x0000_2000;
75         /// Used async_schedule*(), used by module init
76        const PF_USED_ASYNC = 0x0000_4000;
77        ///  This thread should not be frozen
78        const PF_NOFREEZE = 0x0000_8000;
79        /// Frozen for system suspend
80        const PF_FROZEN = 0x0001_0000;
81        /// I am kswapd
82        const PF_KSWAPD = 0x0002_0000;
83        /// All allocation requests will inherit GFP_NOFS
84        const PF_MEMALLOC_NOFS = 0x0004_0000;
85        /// All allocation requests will inherit GFP_NOIO
86        const PF_MEMALLOC_NOIO = 0x0008_0000;
87        /// Throttle me less: I clean memory
88        const PF_LESS_THROTTLE = 0x0010_0000;
89        /// I am a kernel thread
90        const PF_KTHREAD = 0x0020_0000;
91        /// Randomize virtual address space
92        const PF_RANDOMIZE = 0x0040_0000;
93        /// Allowed to write to swap
94        const PF_SWAPWRITE = 0x0080_0000;
95        /// Stalled due to lack of memory
96        const PF_MEMSTALL = 0x0100_0000;
97        /// I'm an Usermodehelper process
98        const PF_UMH = 0x0200_0000;
99        /// Userland is not allowed to meddle with cpus_allowed
100        const PF_NO_SETAFFINITY = 0x0400_0000;
101        /// Early kill for mce process policy
102        const PF_MCE_EARLY = 0x0800_0000;
103        /// All allocation request will have _GFP_MOVABLE cleared
104        const PF_MEMALLOC_NOCMA = 0x1000_0000;
105        /// Thread belongs to the rt mutex tester
106        const PF_MUTEX_TESTER = 0x2000_0000;
107        /// Freezer should not count it as freezable
108        const PF_FREEZER_SKIP = 0x4000_0000;
109        /// This thread called freeze_processes() and should not be frozen
110        const PF_SUSPEND_TASK = 0x8000_0000;
111
112    }
113}
114
115bitflags! {
116    /// See the [coredump_filter()](struct.Process.html#method.coredump_filter) method.
117    #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
118    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
119    pub struct CoredumpFlags: u32 {
120        const ANONYMOUS_PRIVATE_MAPPINGS = 0x01;
121        const ANONYMOUS_SHARED_MAPPINGS = 0x02;
122        const FILEBACKED_PRIVATE_MAPPINGS = 0x04;
123        const FILEBACKED_SHARED_MAPPINGS = 0x08;
124        const ELF_HEADERS = 0x10;
125        const PROVATE_HUGEPAGES = 0x20;
126        const SHARED_HUGEPAGES = 0x40;
127        const PRIVATE_DAX_PAGES = 0x80;
128        const SHARED_DAX_PAGES = 0x100;
129    }
130}
131
132bitflags! {
133    /// The permissions a process has on memory map entries.
134    ///
135    /// Note that the `SHARED` and `PRIVATE` are mutually exclusive, so while you can
136    /// use `MMPermissions::all()` to construct an instance that has all bits set,
137    /// this particular value would never been seen in procfs.
138    #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
139    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)]
140    pub struct MMPermissions: u8 {
141        /// No permissions
142        const NONE = 0;
143        /// Read permission
144        const READ = 1 << 0;
145        /// Write permission
146        const WRITE = 1 << 1;
147        /// Execute permission
148        const EXECUTE = 1 << 2;
149        /// Memory is shared with another process.
150        ///
151        /// Mutually exclusive with PRIVATE.
152        const SHARED = 1 << 3;
153        /// Memory is private (and copy-on-write)
154        ///
155        /// Mutually exclusive with SHARED.
156        const PRIVATE = 1 << 4;
157    }
158}
159
160impl MMPermissions {
161    fn from_ascii_char(b: u8) -> Self {
162        match b {
163            b'r' => Self::READ,
164            b'w' => Self::WRITE,
165            b'x' => Self::EXECUTE,
166            b's' => Self::SHARED,
167            b'p' => Self::PRIVATE,
168            _ => Self::NONE,
169        }
170    }
171    /// Returns this permission map as a 4-character string, similar to what you
172    /// might see in `/proc/\<pid\>/maps`.
173    ///
174    /// Note that the SHARED and PRIVATE bits are mutually exclusive, so this
175    /// string is 4 characters long, not 5.
176    pub fn as_str(&self) -> String {
177        let mut s = String::with_capacity(4);
178        s.push(if self.contains(Self::READ) { 'r' } else { '-' });
179        s.push(if self.contains(Self::WRITE) { 'w' } else { '-' });
180        s.push(if self.contains(Self::EXECUTE) { 'x' } else { '-' });
181        s.push(if self.contains(Self::SHARED) {
182            's'
183        } else if self.contains(Self::PRIVATE) {
184            'p'
185        } else {
186            '-'
187        });
188
189        s
190    }
191}
192
193impl FromStr for MMPermissions {
194    type Err = std::convert::Infallible;
195
196    fn from_str(s: &str) -> Result<Self, Self::Err> {
197        // Only operate on ASCII (byte) values
198        Ok(s.bytes()
199            .map(Self::from_ascii_char)
200            .fold(Self::default(), std::ops::BitOr::bitor))
201    }
202}
203
204bitflags! {
205    /// Represents the kernel flags associated with the virtual memory area.
206    /// The names of these flags are just those you'll find in the man page, but in upper case.
207    #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
208    #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Default)]
209    pub struct VmFlags: u32 {
210        /// No flags
211        const NONE = 0;
212        /// Readable
213        const RD = 1 << 0;
214        /// Writable
215        const WR = 1 << 1;
216        /// Executable
217        const EX = 1 << 2;
218        /// Shared
219        const SH = 1 << 3;
220        /// May read
221        const MR = 1 << 4;
222        /// May write
223        const MW = 1 << 5;
224        /// May execute
225        const ME = 1 << 6;
226        /// May share
227        const MS = 1 << 7;
228        /// Stack segment grows down
229        const GD = 1 << 8;
230        /// Pure PFN range
231        const PF = 1 << 9;
232        /// Disable write to the mapped file
233        const DW = 1 << 10;
234        /// Pages are locked in memory
235        const LO = 1 << 11;
236        /// Memory mapped I/O area
237        const IO = 1 << 12;
238        /// Sequential read advise provided
239        const SR = 1 << 13;
240        /// Random read provided
241        const RR = 1 << 14;
242        /// Do not copy area on fork
243        const DC = 1 << 15;
244        /// Do not expand area on remapping
245        const DE = 1 << 16;
246        /// Area is accountable
247        const AC = 1 << 17;
248        /// Swap space is not reserved for the area
249        const NR = 1 << 18;
250        /// Area uses huge TLB pages
251        const HT = 1 << 19;
252        /// Perform synchronous page faults (since Linux 4.15)
253        const SF = 1 << 20;
254        /// Non-linear mapping (removed in Linux 4.0)
255        const NL = 1 << 21;
256        /// Architecture specific flag
257        const AR = 1 << 22;
258        /// Wipe on fork (since Linux 4.14)
259        const WF = 1 << 23;
260        /// Do not include area into core dump
261        const DD = 1 << 24;
262        /// Soft-dirty flag (since Linux 3.13)
263        const SD = 1 << 25;
264        /// Mixed map area
265        const MM = 1 << 26;
266        /// Huge page advise flag
267        const HG = 1 << 27;
268        /// No-huge page advise flag
269        const NH = 1 << 28;
270        /// Mergeable advise flag
271        const MG = 1 << 29;
272        /// Userfaultfd missing pages tracking (since Linux 4.3)
273        const UM = 1 << 30;
274        /// Userfaultfd wprotect pages tracking (since Linux 4.3)
275        const UW = 1 << 31;
276    }
277}
278
279impl VmFlags {
280    fn from_str(flag: &str) -> Self {
281        if flag.len() != 2 {
282            return VmFlags::NONE;
283        }
284
285        match flag {
286            "rd" => VmFlags::RD,
287            "wr" => VmFlags::WR,
288            "ex" => VmFlags::EX,
289            "sh" => VmFlags::SH,
290            "mr" => VmFlags::MR,
291            "mw" => VmFlags::MW,
292            "me" => VmFlags::ME,
293            "ms" => VmFlags::MS,
294            "gd" => VmFlags::GD,
295            "pf" => VmFlags::PF,
296            "dw" => VmFlags::DW,
297            "lo" => VmFlags::LO,
298            "io" => VmFlags::IO,
299            "sr" => VmFlags::SR,
300            "rr" => VmFlags::RR,
301            "dc" => VmFlags::DC,
302            "de" => VmFlags::DE,
303            "ac" => VmFlags::AC,
304            "nr" => VmFlags::NR,
305            "ht" => VmFlags::HT,
306            "sf" => VmFlags::SF,
307            "nl" => VmFlags::NL,
308            "ar" => VmFlags::AR,
309            "wf" => VmFlags::WF,
310            "dd" => VmFlags::DD,
311            "sd" => VmFlags::SD,
312            "mm" => VmFlags::MM,
313            "hg" => VmFlags::HG,
314            "nh" => VmFlags::NH,
315            "mg" => VmFlags::MG,
316            "um" => VmFlags::UM,
317            "uw" => VmFlags::UW,
318            _ => VmFlags::NONE,
319        }
320    }
321}
322
323/// Represents the state of a process.
324#[derive(Debug, Clone, Copy, Eq, PartialEq)]
325pub enum ProcState {
326    /// Running (R)
327    Running,
328    /// Sleeping in an interruptible wait (S)
329    Sleeping,
330    /// Waiting in uninterruptible disk sleep (D)
331    Waiting,
332    /// Zombie (Z)
333    Zombie,
334    /// Stopped (on a signal) (T)
335    ///
336    /// Or before Linux 2.6.33, trace stopped
337    Stopped,
338    /// Tracing stop (t) (Linux 2.6.33 onward)
339    Tracing,
340    /// Dead (X)
341    Dead,
342    /// Wakekill (K) (Linux 2.6.33 to 3.13 only)
343    Wakekill,
344    /// Waking (W) (Linux 2.6.33 to 3.13 only)
345    Waking,
346    /// Parked (P) (Linux 3.9 to 3.13 only)
347    Parked,
348    /// Idle (I)
349    Idle,
350}
351
352impl ProcState {
353    pub fn from_char(c: char) -> Option<ProcState> {
354        match c {
355            'R' => Some(ProcState::Running),
356            'S' => Some(ProcState::Sleeping),
357            'D' => Some(ProcState::Waiting),
358            'Z' => Some(ProcState::Zombie),
359            'T' => Some(ProcState::Stopped),
360            't' => Some(ProcState::Tracing),
361            'X' | 'x' => Some(ProcState::Dead),
362            'K' => Some(ProcState::Wakekill),
363            'W' => Some(ProcState::Waking),
364            'P' => Some(ProcState::Parked),
365            'I' => Some(ProcState::Idle),
366            _ => None,
367        }
368    }
369}
370
371impl FromStr for ProcState {
372    type Err = ProcError;
373    fn from_str(s: &str) -> Result<ProcState, ProcError> {
374        ProcState::from_char(expect!(s.chars().next(), "empty string"))
375            .ok_or_else(|| build_internal_error!("failed to convert"))
376    }
377}
378
379/// This struct contains I/O statistics for the process, built from `/proc/<pid>/io`
380///
381/// #  Note
382///
383/// In the current implementation, things are a bit racy on 32-bit systems: if process A
384/// reads process B's `/proc/<pid>/io` while process  B is updating one of these 64-bit
385/// counters, process A could see an intermediate result.
386#[derive(Debug, Copy, Clone)]
387#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
388pub struct Io {
389    /// Characters read
390    ///
391    /// The number of bytes which this task has caused to be read from storage.  This is simply the
392    /// sum of bytes which this process passed to read(2)  and  similar system calls.  It includes
393    /// things such as terminal I/O and is unaffected by whether or not actual physical disk I/O
394    /// was required (the read might have been satisfied from pagecache).
395    pub rchar: u64,
396
397    /// characters written
398    ///
399    /// The number of bytes which this task has caused, or shall cause to be written to disk.
400    /// Similar caveats apply here as with rchar.
401    pub wchar: u64,
402    /// read syscalls
403    ///
404    /// Attempt to count the number of write I/O operations—that is, system calls such as write(2)
405    /// and pwrite(2).
406    pub syscr: u64,
407    /// write syscalls
408    ///
409    /// Attempt to count the number of write I/O operations—that is, system calls such as write(2)
410    /// and pwrite(2).
411    pub syscw: u64,
412    /// bytes read
413    ///
414    /// Attempt to count the number of bytes which this process really did cause to be fetched from
415    /// the storage layer.  This is accurate  for block-backed filesystems.
416    pub read_bytes: u64,
417    /// bytes written
418    ///
419    /// Attempt to count the number of bytes which this process caused to be sent to the storage layer.
420    pub write_bytes: u64,
421    /// Cancelled write bytes.
422    ///
423    /// The  big inaccuracy here is truncate.  If a process writes 1MB to a file and then deletes
424    /// the file, it will in fact perform no write‐ out.  But it will have been accounted as having
425    /// caused 1MB of write.  In other words: this field represents the number of bytes which this
426    /// process caused to not happen, by truncating pagecache.  A task can cause "negative" I/O too.
427    /// If this task truncates some dirty pagecache, some I/O which another task has been accounted
428    /// for (in its write_bytes) will not be happening.
429    pub cancelled_write_bytes: u64,
430}
431
432#[derive(Debug, PartialEq, Eq, Clone, Hash)]
433#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
434pub enum MMapPath {
435    /// The file that is backing the mapping.
436    Path(PathBuf),
437    /// The process's heap.
438    Heap,
439    /// The initial process's (also known as the main thread's) stack.
440    Stack,
441    /// A thread's stack (where the `<tid>` is a thread ID).  It corresponds to the
442    /// `/proc/<pid>/task/<tid>/` path.
443    ///
444    /// (since Linux 3.4)
445    TStack(u32),
446    /// The virtual dynamically linked shared object.
447    Vdso,
448    /// Shared kernel variables
449    Vvar,
450    /// obsolete virtual syscalls, succeeded by vdso
451    Vsyscall,
452    /// rollup memory mappings, from `/proc/<pid>/smaps_rollup`
453    Rollup,
454    /// An anonymous mapping as obtained via mmap(2).
455    Anonymous,
456    /// Shared memory segment. The i32 value corresponds to [Shm.key](Shm::key), while [MemoryMap.inode](MemoryMap::inode) corresponds to [Shm.shmid](Shm::shmid)
457    Vsys(i32),
458    /// Some other pseudo-path
459    Other(String),
460}
461
462impl MMapPath {
463    pub fn from(path: &str) -> ProcResult<MMapPath> {
464        Ok(match path.trim() {
465            "" => MMapPath::Anonymous,
466            "[heap]" => MMapPath::Heap,
467            "[stack]" => MMapPath::Stack,
468            "[vdso]" => MMapPath::Vdso,
469            "[vvar]" => MMapPath::Vvar,
470            "[vsyscall]" => MMapPath::Vsyscall,
471            "[rollup]" => MMapPath::Rollup,
472            x if x.starts_with("[stack:") => {
473                let mut s = x[1..x.len() - 1].split(':');
474                let tid = from_str!(u32, expect!(s.nth(1)));
475                MMapPath::TStack(tid)
476            }
477            x if x.starts_with('[') && x.ends_with(']') => MMapPath::Other(x[1..x.len() - 1].to_string()),
478            x if x.starts_with("/SYSV") => MMapPath::Vsys(u32::from_str_radix(&x[5..13], 16)? as i32), // 32bits signed hex. /SYSVaabbccdd (deleted)
479            x => MMapPath::Path(PathBuf::from(x)),
480        })
481    }
482}
483
484/// Represents all entries in a `/proc/<pid>/maps` or `/proc/<pid>/smaps` file.
485#[derive(Debug, PartialEq, Eq, Clone)]
486#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
487#[non_exhaustive]
488pub struct MemoryMaps(pub Vec<MemoryMap>);
489
490impl crate::FromBufRead for MemoryMaps {
491    /// The data should be formatted according to procfs /proc/pid/{maps,smaps,smaps_rollup}.
492    fn from_buf_read<R: BufRead>(reader: R) -> ProcResult<Self> {
493        let mut memory_maps = Vec::new();
494
495        let mut line_iter = reader.lines().map(|r| r.map_err(|_| ProcError::Incomplete(None)));
496        let mut current_memory_map: Option<MemoryMap> = None;
497        while let Some(line) = line_iter.next().transpose()? {
498            // Assumes all extension fields (in `/proc/<pid>/smaps`) start with a capital letter,
499            // which seems to be the case.
500            if line.starts_with(|c: char| c.is_ascii_uppercase()) {
501                match current_memory_map.as_mut() {
502                    None => return Err(ProcError::Incomplete(None)),
503                    Some(mm) => {
504                        // This is probably an attribute
505                        if line.starts_with("VmFlags") {
506                            let flags = line.split_ascii_whitespace();
507                            let flags = flags.skip(1); // Skips the `VmFlags:` part since we don't need it.
508
509                            let flags = flags
510                                .map(VmFlags::from_str)
511                                // FUTURE: use `Iterator::reduce`
512                                .fold(VmFlags::NONE, std::ops::BitOr::bitor);
513
514                            mm.extension.vm_flags = flags;
515                        } else {
516                            let mut parts = line.split_ascii_whitespace();
517
518                            let key = parts.next();
519                            let value = parts.next();
520
521                            if let (Some(k), Some(v)) = (key, value) {
522                                // While most entries do have one, not all of them do.
523                                let size_suffix = parts.next();
524
525                                // Limited poking at /proc/<pid>/smaps and then checking if "MB", "GB", and "TB" appear in the C file that is
526                                // supposedly responsible for creating smaps, has lead me to believe that the only size suffixes we'll ever encounter
527                                // "kB", which is most likely kibibytes. Actually checking if the size suffix is any of the above is a way to
528                                // future-proof the code, but I am not sure it is worth doing so.
529                                let size_multiplier = if size_suffix.is_some() { 1024 } else { 1 };
530
531                                let v = v.parse::<u64>().map_err(|_| {
532                                    ProcError::Other("Value in `Key: Value` pair was not actually a number".into())
533                                })?;
534
535                                // This ignores the case when our Key: Value pairs are really Key Value pairs. Is this a good idea?
536                                let k = k.trim_end_matches(':');
537
538                                mm.extension.map.insert(k.into(), v * size_multiplier);
539                            }
540                        }
541                    }
542                }
543            } else {
544                if let Some(mm) = current_memory_map.take() {
545                    memory_maps.push(mm);
546                }
547                current_memory_map = Some(MemoryMap::from_line(&line)?);
548            }
549        }
550        if let Some(mm) = current_memory_map.take() {
551            memory_maps.push(mm);
552        }
553
554        Ok(MemoryMaps(memory_maps))
555    }
556}
557
558impl MemoryMaps {
559    /// Return an iterator over [MemoryMap].
560    pub fn iter(&self) -> std::slice::Iter<MemoryMap> {
561        self.0.iter()
562    }
563
564    pub fn len(&self) -> usize {
565        self.0.len()
566    }
567}
568
569impl<'a> IntoIterator for &'a MemoryMaps {
570    type IntoIter = std::slice::Iter<'a, MemoryMap>;
571    type Item = &'a MemoryMap;
572
573    fn into_iter(self) -> Self::IntoIter {
574        self.iter()
575    }
576}
577
578impl IntoIterator for MemoryMaps {
579    type IntoIter = std::vec::IntoIter<MemoryMap>;
580    type Item = MemoryMap;
581
582    fn into_iter(self) -> Self::IntoIter {
583        self.0.into_iter()
584    }
585}
586
587/// Represents an entry in a `/proc/<pid>/maps` or `/proc/<pid>/smaps` file.
588#[derive(Debug, PartialEq, Eq, Clone)]
589#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
590pub struct MemoryMap {
591    /// The address space in the process that the mapping occupies.
592    pub address: (u64, u64),
593    pub perms: MMPermissions,
594    /// The offset into the file/whatever
595    pub offset: u64,
596    /// The device (major, minor)
597    pub dev: (i32, i32),
598    /// The inode on that device
599    ///
600    /// 0 indicates that no inode is associated with the memory region, as would be the case with
601    /// BSS (uninitialized data).
602    pub inode: u64,
603    pub pathname: MMapPath,
604    /// Memory mapping extension information, populated when parsing `/proc/<pid>/smaps`.
605    ///
606    /// The members will be `Default::default()` (empty/none) when the information isn't available.
607    pub extension: MMapExtension,
608}
609
610impl MemoryMap {
611    fn from_line(line: &str) -> ProcResult<MemoryMap> {
612        let mut s = line.splitn(6, ' ');
613        let address = expect!(s.next());
614        let perms = expect!(s.next());
615        let offset = expect!(s.next());
616        let dev = expect!(s.next());
617        let inode = expect!(s.next());
618        let path = expect!(s.next());
619
620        Ok(MemoryMap {
621            address: split_into_num(address, '-', 16)?,
622            perms: perms.parse()?,
623            offset: from_str!(u64, offset, 16),
624            dev: split_into_num(dev, ':', 16)?,
625            inode: from_str!(u64, inode),
626            pathname: MMapPath::from(path)?,
627            extension: Default::default(),
628        })
629    }
630}
631
632/// Represents the information about a specific mapping as presented in /proc/\<pid\>/smaps
633#[derive(Default, Debug, PartialEq, Eq, Clone)]
634#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
635pub struct MMapExtension {
636    /// Key-value pairs that may represent statistics about memory usage, or other interesting things,
637    /// such a "ProtectionKey" (if you're on X86 and that kernel config option was specified).
638    ///
639    /// Note that should a key-value pair represent a memory usage statistic, it will be in bytes.
640    ///
641    /// Check your manpage for more information
642    pub map: HashMap<String, u64>,
643    /// Kernel flags associated with the virtual memory area
644    ///
645    /// (since Linux 3.8)
646    pub vm_flags: VmFlags,
647}
648
649impl MMapExtension {
650    /// Return whether the extension information is empty.
651    pub fn is_empty(&self) -> bool {
652        self.map.is_empty() && self.vm_flags == VmFlags::NONE
653    }
654}
655
656impl crate::FromBufRead for Io {
657    fn from_buf_read<R: BufRead>(reader: R) -> ProcResult<Self> {
658        let mut map = HashMap::new();
659
660        for line in reader.lines() {
661            let line = line?;
662            if line.is_empty() || !line.contains(' ') {
663                continue;
664            }
665            let mut s = line.split_whitespace();
666            let field = expect!(s.next());
667            let value = expect!(s.next());
668
669            let value = from_str!(u64, value);
670
671            map.insert(field[..field.len() - 1].to_string(), value);
672        }
673        let io = Io {
674            rchar: expect!(map.remove("rchar")),
675            wchar: expect!(map.remove("wchar")),
676            syscr: expect!(map.remove("syscr")),
677            syscw: expect!(map.remove("syscw")),
678            read_bytes: expect!(map.remove("read_bytes")),
679            write_bytes: expect!(map.remove("write_bytes")),
680            cancelled_write_bytes: expect!(map.remove("cancelled_write_bytes")),
681        };
682
683        assert!(!cfg!(test) || map.is_empty(), "io map is not empty: {:#?}", map);
684
685        Ok(io)
686    }
687}
688
689/// Describes a file descriptor opened by a process.
690#[derive(Clone, Debug)]
691#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
692pub enum FDTarget {
693    /// A file or device
694    Path(PathBuf),
695    /// A socket type, with an inode
696    Socket(u64),
697    Net(u64),
698    Pipe(u64),
699    /// A file descriptor that have no corresponding inode.
700    AnonInode(String),
701    /// A memfd file descriptor with a name.
702    MemFD(String),
703    /// Some other file descriptor type, with an inode.
704    Other(String, u64),
705}
706
707impl FromStr for FDTarget {
708    type Err = ProcError;
709    fn from_str(s: &str) -> Result<FDTarget, ProcError> {
710        // helper function that removes the first and last character
711        fn strip_first_last(s: &str) -> ProcResult<&str> {
712            if s.len() > 2 {
713                let mut c = s.chars();
714                // remove the first and last characters
715                let _ = c.next();
716                let _ = c.next_back();
717                Ok(c.as_str())
718            } else {
719                Err(ProcError::Incomplete(None))
720            }
721        }
722
723        if !s.starts_with('/') && s.contains(':') {
724            let mut s = s.split(':');
725            let fd_type = expect!(s.next());
726            match fd_type {
727                "socket" => {
728                    let inode = expect!(s.next(), "socket inode");
729                    let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
730                    Ok(FDTarget::Socket(inode))
731                }
732                "net" => {
733                    let inode = expect!(s.next(), "net inode");
734                    let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
735                    Ok(FDTarget::Net(inode))
736                }
737                "pipe" => {
738                    let inode = expect!(s.next(), "pipe inode");
739                    let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
740                    Ok(FDTarget::Pipe(inode))
741                }
742                "anon_inode" => Ok(FDTarget::AnonInode(expect!(s.next(), "anon inode").to_string())),
743                "" => Err(ProcError::Incomplete(None)),
744                x => {
745                    let inode = expect!(s.next(), "other inode");
746                    let inode = expect!(u64::from_str_radix(strip_first_last(inode)?, 10));
747                    Ok(FDTarget::Other(x.to_string(), inode))
748                }
749            }
750        } else if let Some(s) = s.strip_prefix("/memfd:") {
751            Ok(FDTarget::MemFD(s.to_string()))
752        } else {
753            Ok(FDTarget::Path(PathBuf::from(s)))
754        }
755    }
756}
757
758/// Provides information about memory usage, measured in pages.
759#[derive(Debug, Clone, Copy)]
760#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
761pub struct StatM {
762    /// Total program size, measured in pages
763    ///
764    /// (same as VmSize in /proc/\<pid\>/status)
765    pub size: u64,
766    /// Resident set size, measured in pages
767    ///
768    /// (same as VmRSS in /proc/\<pid\>/status)
769    pub resident: u64,
770    /// number of resident shared pages (i.e., backed by a file)
771    ///
772    /// (same as RssFile+RssShmem in /proc/\<pid\>/status)
773    pub shared: u64,
774    /// Text (code)
775    pub text: u64,
776    /// library (unused since Linux 2.6; always 0)
777    pub lib: u64,
778    /// data + stack
779    pub data: u64,
780    /// dirty pages (unused since Linux 2.6; always 0)
781    pub dt: u64,
782}
783
784impl crate::FromRead for StatM {
785    fn from_read<R: Read>(mut r: R) -> ProcResult<Self> {
786        let mut line = String::new();
787        r.read_to_string(&mut line)?;
788        let mut s = line.split_whitespace();
789
790        let size = expect!(from_iter(&mut s));
791        let resident = expect!(from_iter(&mut s));
792        let shared = expect!(from_iter(&mut s));
793        let text = expect!(from_iter(&mut s));
794        let lib = expect!(from_iter(&mut s));
795        let data = expect!(from_iter(&mut s));
796        let dt = expect!(from_iter(&mut s));
797
798        if cfg!(test) {
799            assert!(s.next().is_none());
800        }
801
802        Ok(StatM {
803            size,
804            resident,
805            shared,
806            text,
807            lib,
808            data,
809            dt,
810        })
811    }
812}
813
814#[cfg(test)]
815mod tests {
816    use super::*;
817
818    #[test]
819    fn parse_memory_map_permissions() {
820        use MMPermissions as P;
821        assert_eq!("rw-p".parse(), Ok(P::READ | P::WRITE | P::PRIVATE));
822        assert_eq!("r-xs".parse(), Ok(P::READ | P::EXECUTE | P::SHARED));
823        assert_eq!("----".parse(), Ok(P::NONE));
824
825        assert_eq!((P::READ | P::WRITE | P::PRIVATE).as_str(), "rw-p");
826        assert_eq!((P::READ | P::EXECUTE | P::SHARED).as_str(), "r-xs");
827        assert_eq!(P::NONE.as_str(), "----");
828    }
829}