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