Skip to content

Commit 2c7bb2a

Browse files
bors[bot]kov
andauthored
Merge #1747
1747: Add getrusage wrapper r=rtzoeller a=kov 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]> Co-authored-by: Gustavo Noronha Silva <[email protected]>
2 parents b6df05d + 9313a18 commit 2c7bb2a

File tree

2 files changed

+182
-6
lines changed

2 files changed

+182
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1919
(#[1703](https://github.com/nix-rust/nix/pull/1703))
2020
- Added `ptrace::read_user` and `ptrace::write_user` for Linux.
2121
(#[1697](https://github.com/nix-rust/nix/pull/1697))
22+
- Added `getrusage` and helper types `UsageWho` and `Usage`
23+
(#[1747](https://github.com/nix-rust/nix/pull/1747))
2224

2325
### Changed
2426

src/sys/resource.rs

Lines changed: 180 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
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;
6+
use crate::sys::time::TimeVal;
57
use crate::Result;
68
pub use libc::rlim_t;
79
use std::mem;
@@ -19,7 +21,7 @@ cfg_if! {
1921
target_os = "dragonfly",
2022
all(target_os = "linux", not(target_env = "gnu"))
2123
))]{
22-
use libc::{c_int, rlimit};
24+
use libc::rlimit;
2325
}
2426
}
2527

@@ -242,11 +244,7 @@ pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
242244
/// [`Resource`]: enum.Resource.html
243245
///
244246
/// 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<()> {
247+
pub fn setrlimit(resource: Resource, soft_limit: rlim_t, hard_limit: rlim_t) -> Result<()> {
250248
let new_rlim = rlimit {
251249
rlim_cur: soft_limit,
252250
rlim_max: hard_limit,
@@ -261,3 +259,179 @@ pub fn setrlimit(
261259

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

0 commit comments

Comments
 (0)