Skip to main content

tuwunel_core/utils/sys/
usage.rs

1//! Process and thread resource-usage utilities.
2//!
3//! The helpers expose memory measurements and operating-system usage records.
4//! Platform-specific implementations provide neutral fallback values where
5//! native accounting is unavailable.
6
7#[cfg(unix)]
8use nix::sys::resource::{Usage as NixUsage, UsageWho, getrusage};
9
10use crate::{Result, expected};
11
12/// Platform representation of process resource usage.
13///
14/// On Unix this aliases nix's `Usage`, populated by `getrusage()`. Platforms
15/// without `getrusage()` use a zero-field `Debug` stub so tracing fields such
16/// as `?resource_usage` remain portable.
17#[cfg(unix)]
18pub type Usage = NixUsage;
19
20/// Portable resource-usage stub for platforms without `getrusage()`.
21///
22/// The zero-field value keeps tracing and call sites portable. It contains no
23/// process or thread measurements.
24#[cfg(not(unix))]
25#[derive(Debug, Default, Clone, Copy)]
26pub struct Usage;
27
28/// Returns the process's virtual memory size in bytes, or zero outside Linux.
29///
30/// The value is the first field of Linux `/proc/self/statm`, scaled by the
31/// system page size. It is sampled separately from the other memory fields.
32///
33/// # Panics
34///
35/// On Linux, panics if `statm` is malformed, exceeds parser capacity, or lacks
36/// the requested field. Panics if converting that page count overflows `usize`
37/// bytes.
38pub fn virt() -> Result<usize> {
39	Ok(statm_bytes()?
40		.next()
41		.expect("incomplete statm contents"))
42}
43
44/// Returns the process's resident memory size in bytes, or zero outside Linux.
45///
46/// The value is the second field of Linux `/proc/self/statm`, scaled by the
47/// system page size. It is sampled separately from the other memory fields.
48///
49/// # Panics
50///
51/// On Linux, panics if `statm` is malformed, exceeds parser capacity, or lacks
52/// the requested field. Panics if converting that page count overflows `usize`
53/// bytes.
54pub fn res() -> Result<usize> {
55	Ok(statm_bytes()?
56		.nth(1)
57		.expect("incomplete statm contents"))
58}
59
60/// Returns shared resident memory size in bytes, or zero outside Linux.
61///
62/// The value is the third field of Linux `/proc/self/statm`, scaled by the
63/// system page size. It is sampled separately from the other memory fields.
64///
65/// # Panics
66///
67/// On Linux, panics if `statm` is malformed, exceeds parser capacity, or lacks
68/// the requested field. Panics if converting that page count overflows `usize`
69/// bytes.
70pub fn shm() -> Result<usize> {
71	Ok(statm_bytes()?
72		.nth(2)
73		.expect("incomplete statm contents"))
74}
75
76/// Returns the process's resident code size in bytes, or zero outside Linux.
77///
78/// The value is the fourth field of Linux `/proc/self/statm`, scaled by the
79/// system page size. It represents the resident text segment.
80///
81/// # Panics
82///
83/// On Linux, panics if `statm` is malformed, exceeds parser capacity, or lacks
84/// the requested field. Panics if converting that page count overflows `usize`
85/// bytes.
86pub fn code() -> Result<usize> {
87	Ok(statm_bytes()?
88		.nth(3)
89		.expect("incomplete statm contents"))
90}
91
92/// Returns the process's data and stack size in bytes, or zero outside Linux.
93///
94/// The value is the sixth field of Linux `/proc/self/statm`, scaled by the
95/// system page size. It is sampled separately from the other memory fields.
96///
97/// # Panics
98///
99/// On Linux, panics if `statm` is malformed, exceeds parser capacity, or lacks
100/// the requested field. Panics if converting that page count overflows `usize`
101/// bytes.
102pub fn data() -> Result<usize> {
103	Ok(statm_bytes()?
104		.nth(5)
105		.expect("incomplete statm contents"))
106}
107
108/// Returns the `statm` page counts converted to bytes.
109///
110/// Each page count is multiplied by the system page size as the iterator is
111/// consumed. The field order remains the Linux `statm` order.
112///
113/// # Panics
114///
115/// On Linux, panics before returning if `statm` is malformed or exceeds parser
116/// capacity. Panics while iterating if converting a page count overflows
117/// `usize` bytes.
118#[inline]
119pub fn statm_bytes() -> Result<impl Iterator<Item = usize>> {
120	let page_size = super::page_size()?;
121
122	Ok(statm()?.map(move |pages| expected!(pages * page_size)))
123}
124
125/// Returns process memory statistics as page counts.
126///
127/// Linux parses a snapshot of `/proc/self/statm` in kernel field order. The
128/// returned iterator owns the parsed counts.
129///
130/// # Panics
131///
132/// Panics when `statm` contains non-UTF-8 or non-integer data, or more than 12
133/// fields.
134#[cfg(target_os = "linux")]
135#[inline]
136pub fn statm() -> Result<impl Iterator<Item = usize>> {
137	use std::{fs::File, io::Read, str};
138
139	use crate::{Error, arrayvec::ArrayVec};
140
141	File::open("/proc/self/statm")
142		.map_err(Error::from)
143		.and_then(|mut fp| {
144			let mut buf = [0; 96];
145			let len = fp.read(&mut buf)?;
146			let vals = str::from_utf8(&buf[0..len])
147				.expect("non-utf8 content in statm")
148				.split_ascii_whitespace()
149				.map(|val| {
150					val.parse()
151						.expect("non-integer value in statm contents")
152				})
153				.collect::<ArrayVec<usize, 12>>();
154
155			Ok(vals.into_iter())
156		})
157}
158
159/// Returns process memory statistics as page counts.
160///
161/// Non-Linux platforms return six zero counts in Linux `statm` field order. No
162/// operating-system query is performed.
163#[cfg(not(target_os = "linux"))]
164#[inline]
165pub fn statm() -> Result<impl Iterator<Item = usize>> { Ok([0, 0, 0, 0, 0, 0].into_iter()) }
166
167/// Returns resource usage for the current process.
168///
169/// Unix platforms obtain a fresh `RUSAGE_SELF` snapshot from `getrusage()`.
170/// The measurement includes resources consumed by the process at call time.
171#[cfg(unix)]
172pub fn usage() -> Result<Usage> { getrusage(UsageWho::RUSAGE_SELF).map_err(Into::into) }
173
174/// Returns the portable process resource-usage stub.
175///
176/// Platforms without `getrusage()` expose no measurements through this API.
177/// The returned zero-field value keeps callers platform-independent.
178#[cfg(not(unix))]
179pub fn usage() -> Result<Usage> { Ok(Usage) }
180
181#[cfg(any(
182	target_os = "linux",
183	target_os = "freebsd",
184	target_os = "openbsd"
185))]
186/// Returns resource usage for the current thread when the platform supports it.
187///
188/// Linux, FreeBSD, and OpenBSD report thread-specific usage. Other Unix
189/// platforms fall back to process-wide usage. Platforms without `getrusage()`
190/// return the zero-field [`Usage`] stub.
191pub fn thread_usage() -> Result<Usage> { getrusage(UsageWho::RUSAGE_THREAD).map_err(Into::into) }
192
193#[cfg(all(
194	unix,
195	not(any(
196		target_os = "linux",
197		target_os = "freebsd",
198		target_os = "openbsd"
199	))
200))]
201/// Returns resource usage for the current thread when the platform supports it.
202///
203/// Linux, FreeBSD, and OpenBSD report thread-specific usage. Other Unix
204/// platforms fall back to process-wide usage. Platforms without `getrusage()`
205/// return the zero-field [`Usage`] stub.
206pub fn thread_usage() -> Result<Usage> { getrusage(UsageWho::RUSAGE_SELF).map_err(Into::into) }
207
208#[cfg(not(unix))]
209/// Returns resource usage for the current thread when the platform supports it.
210///
211/// Linux, FreeBSD, and OpenBSD report thread-specific usage. Other Unix
212/// platforms fall back to process-wide usage. Platforms without `getrusage()`
213/// return the zero-field [`Usage`] stub.
214pub fn thread_usage() -> Result<Usage> { Ok(Usage) }