Skip to content

Commit 83927a4

Browse files
committed
Add getrusage wrapper
Includes an enum to specify what to get resource usage for, and a new struct that provides a more readable view into libc::rusage, including using TimeVal for user and system CPU time. Signed-off-by: Gustavo Noronha Silva <[email protected]>
1 parent 01a5927 commit 83927a4

File tree

1 file changed

+179
-6
lines changed

1 file changed

+179
-6
lines changed

src/sys/resource.rs

Lines changed: 179 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
//! Configure the process resource limits.
22
use cfg_if::cfg_if;
3+
use libc::{c_int, c_long, rusage};
34

45
use crate::errno::Errno;
56
use crate::Result;
67
pub use libc::rlim_t;
78
use std::mem;
89

10+
use super::time::TimeVal;
11+
912
cfg_if! {
1013
if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
1114
use libc::{__rlimit_resource_t, rlimit};
@@ -19,7 +22,7 @@ cfg_if! {
1922
target_os = "dragonfly",
2023
all(target_os = "linux", not(target_env = "gnu"))
2124
))]{
22-
use libc::{c_int, rlimit};
25+
use libc::rlimit;
2326
}
2427
}
2528

@@ -242,11 +245,7 @@ pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
242245
/// [`Resource`]: enum.Resource.html
243246
///
244247
/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
245-
pub fn setrlimit(
246-
resource: Resource,
247-
soft_limit: rlim_t,
248-
hard_limit: rlim_t,
249-
) -> Result<()> {
248+
pub fn setrlimit(resource: Resource, soft_limit: rlim_t, hard_limit: rlim_t) -> Result<()> {
250249
let new_rlim = rlimit {
251250
rlim_cur: soft_limit,
252251
rlim_max: hard_limit,
@@ -261,3 +260,177 @@ pub fn setrlimit(
261260

262261
Errno::result(res).map(drop)
263262
}
263+
264+
libc_enum! {
265+
/// Whose resource usage should be returned by [`getrusage`].
266+
#[repr(i32)]
267+
#[non_exhaustive]
268+
pub enum UsageWho {
269+
/// Resource usage for the current process.
270+
RUSAGE_SELF,
271+
272+
/// Resource usage for all the children that have terminated and been waited for.
273+
RUSAGE_CHILDREN,
274+
275+
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
276+
#[cfg_attr(docsrs, doc(cfg(all())))]
277+
/// Resource usage for the calling thread.
278+
RUSAGE_THREAD,
279+
}
280+
}
281+
282+
/// Output of `getrusage` with information about resource usage. Some of the fields
283+
/// may be unused in some platforms, and will be always zeroed out. See their manuals
284+
/// for details.
285+
#[repr(transparent)]
286+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
287+
pub struct Usage(rusage);
288+
289+
impl AsRef<rusage> for Usage {
290+
fn as_ref(&self) -> &rusage {
291+
&self.0
292+
}
293+
}
294+
295+
impl AsMut<rusage> for Usage {
296+
fn as_mut(&mut self) -> &mut rusage {
297+
&mut self.0
298+
}
299+
}
300+
301+
impl Usage {
302+
/// Total amount of time spent executing in user mode.
303+
pub fn user_time(&self) -> TimeVal {
304+
TimeVal::from(self.0.ru_utime)
305+
}
306+
307+
/// Total amount of time spent executing in kernel mode.
308+
pub fn system_time(&self) -> TimeVal {
309+
TimeVal::from(self.0.ru_stime)
310+
}
311+
312+
/// The resident set size at its peak, in kilobytes.
313+
pub fn max_rss(&self) -> c_long {
314+
self.0.ru_maxrss
315+
}
316+
317+
/// Integral value expressed in kilobytes times ticks of execution indicating
318+
/// the amount of text memory shared with other processes.
319+
pub fn shared_integral(&self) -> c_long {
320+
self.0.ru_ixrss
321+
}
322+
323+
/// Integral value expressed in kilobytes times ticks of execution indicating
324+
/// the amount of unshared memory used by data.
325+
pub fn unshared_data_integral(&self) -> c_long {
326+
self.0.ru_idrss
327+
}
328+
329+
/// Integral value expressed in kilobytes times ticks of execution indicating
330+
/// the amount of unshared memory used for stack space.
331+
pub fn unshared_stack_integral(&self) -> c_long {
332+
self.0.ru_isrss
333+
}
334+
335+
/// Number of page faults that were served without resorting to I/O, with pages
336+
/// that have been allocated previously by the kernel.
337+
pub fn minor_page_faults(&self) -> c_long {
338+
self.0.ru_minflt
339+
}
340+
341+
/// Number of page faults that were served through I/O (i.e. swap).
342+
pub fn major_page_faults(&self) -> c_long {
343+
self.0.ru_majflt
344+
}
345+
346+
/// Number of times all of the memory was fully swapped out.
347+
pub fn full_swaps(&self) -> c_long {
348+
self.0.ru_nswap
349+
}
350+
351+
/// Number of times a read was done from a block device.
352+
pub fn block_reads(&self) -> c_long {
353+
self.0.ru_inblock
354+
}
355+
356+
/// Number of times a write was done to a block device.
357+
pub fn block_writes(&self) -> c_long {
358+
self.0.ru_oublock
359+
}
360+
361+
/// Number of IPC messages sent.
362+
pub fn ipc_sends(&self) -> c_long {
363+
self.0.ru_msgsnd
364+
}
365+
366+
/// Number of IPC messages received.
367+
pub fn ipc_receives(&self) -> c_long {
368+
self.0.ru_msgrcv
369+
}
370+
371+
/// Number of signals received.
372+
pub fn signals(&self) -> c_long {
373+
self.0.ru_nsignals
374+
}
375+
376+
/// Number of times a context switch was voluntarily invoked.
377+
pub fn voluntary_context_switches(&self) -> c_long {
378+
self.0.ru_nvcsw
379+
}
380+
381+
/// Number of times a context switch was imposed by the kernel (usually due to
382+
/// time slice expiring or preemption by a higher priority process).
383+
pub fn involuntary_context_switches(&self) -> c_long {
384+
self.0.ru_nivcsw
385+
}
386+
}
387+
388+
/// Get usage information for a process, its children or the current thread
389+
///
390+
/// Real time information can be obtained for either the current process or (in some
391+
/// systems) thread, but information about children processes is only provided for
392+
/// those that have terminated and been waited for (see [`super::wait::wait`]).
393+
///
394+
/// Some information may be missing depending on the platform, and the way information
395+
/// is provided for children may also vary. Check the manuals for details.
396+
///
397+
/// # References
398+
///
399+
/// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html)
400+
/// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html)
401+
/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage)
402+
/// * [NetBSD](https://man.netbsd.org/getrusage.2)
403+
/// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html)
404+
///
405+
/// [`UsageWho`]: enum.UsageWho.html
406+
///
407+
/// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`].
408+
pub fn getrusage(who: UsageWho) -> Result<Usage> {
409+
unsafe {
410+
let mut rusage = mem::MaybeUninit::<rusage>::uninit();
411+
let res = libc::getrusage(who as c_int, rusage.as_mut_ptr());
412+
Errno::result(res).map(|_| Usage(rusage.assume_init()))
413+
}
414+
}
415+
416+
#[cfg(test)]
417+
mod test {
418+
use super::{getrusage, UsageWho};
419+
420+
#[test]
421+
pub fn test_self_cpu_time() {
422+
// Make sure some CPU time is used. FIXME: replace assert with blackbox once stabilized.
423+
let mut numbers: Vec<i32> = (1..1_000_000).collect();
424+
numbers.iter_mut().for_each(|item| *item *= 2);
425+
426+
assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100);
427+
428+
let usage = getrusage(UsageWho::RUSAGE_SELF).expect("Failed to call getrusage for SELF");
429+
let rusage = usage.as_ref();
430+
431+
let user = usage.user_time();
432+
assert!(user.tv_sec() > 0 || user.tv_usec() > 0);
433+
assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec);
434+
assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec);
435+
}
436+
}

0 commit comments

Comments
 (0)