1
1
//! Configure the process resource limits.
2
2
use cfg_if:: cfg_if;
3
+ use libc:: { c_int, c_long, rusage} ;
3
4
4
5
use crate :: errno:: Errno ;
5
6
use crate :: Result ;
6
7
pub use libc:: rlim_t;
7
8
use std:: mem;
8
9
10
+ use super :: time:: TimeVal ;
11
+
9
12
cfg_if ! {
10
13
if #[ cfg( all( target_os = "linux" , any( target_env = "gnu" , target_env = "uclibc" ) ) ) ] {
11
14
use libc:: { __rlimit_resource_t, rlimit} ;
@@ -19,7 +22,7 @@ cfg_if! {
19
22
target_os = "dragonfly" ,
20
23
all( target_os = "linux" , not( target_env = "gnu" ) )
21
24
) ) ] {
22
- use libc:: { c_int , rlimit} ;
25
+ use libc:: rlimit;
23
26
}
24
27
}
25
28
@@ -242,11 +245,7 @@ pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
242
245
/// [`Resource`]: enum.Resource.html
243
246
///
244
247
/// 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 < ( ) > {
250
249
let new_rlim = rlimit {
251
250
rlim_cur : soft_limit,
252
251
rlim_max : hard_limit,
@@ -261,3 +260,177 @@ pub fn setrlimit(
261
260
262
261
Errno :: result ( res) . map ( drop)
263
262
}
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