@@ -6,6 +6,7 @@ use std::io;
6
6
use std:: marker:: PhantomData ;
7
7
use std:: os:: unix:: process:: CommandExt ;
8
8
use std:: process:: { Child , Command } ;
9
+ use std:: time:: Duration ;
9
10
10
11
use nix:: {
11
12
errno:: Errno ,
@@ -46,8 +47,6 @@ pub type Registers = libc::user_regs_struct;
46
47
/// Extra signal info, such as its cause.
47
48
pub type Siginfo = libc:: siginfo_t ;
48
49
49
- const WALL : Option < WaitPidFlag > = Some ( WaitPidFlag :: __WALL) ;
50
-
51
50
/// Linux constant defined in `include/uapi/linux/elf.h`.
52
51
#[ cfg( target_arch = "aarch64" ) ]
53
52
const NT_PRSTATUS : i32 = 0x1 ;
@@ -343,16 +342,22 @@ pub struct Ptracer {
343
342
/// Ptrace options that will be applied to tracees, by default.
344
343
options : Options ,
345
344
345
+ /// Time to sleep for before polling tracees for new events.
346
+ poll_delay : Duration ,
347
+
346
348
/// Known tracees, and their state.
347
349
tracees : BTreeMap < i32 , State > ,
348
350
}
349
351
352
+ const DEFAULT_POLL_DELAY : Duration = Duration :: from_millis ( 100 ) ;
353
+
350
354
impl Ptracer {
351
355
pub fn new ( ) -> Self {
352
356
let options = Options :: all ( ) ;
357
+ let poll_delay = DEFAULT_POLL_DELAY ;
353
358
let tracees = BTreeMap :: new ( ) ;
354
359
355
- Self { options, tracees }
360
+ Self { options, poll_delay , tracees }
356
361
}
357
362
358
363
/// Returns a reference to the default ptrace options applied to newly-spawned tracees.
@@ -365,6 +370,16 @@ impl Ptracer {
365
370
& mut self . options
366
371
}
367
372
373
+ /// Returns a reference to the poll delay.
374
+ pub fn poll_delay ( & self ) -> & Duration {
375
+ & self . poll_delay
376
+ }
377
+
378
+ /// Returns a mutable reference to the poll delay.
379
+ pub fn poll_delay_mut ( & mut self ) -> & mut Duration {
380
+ & mut self . poll_delay
381
+ }
382
+
368
383
/// Resume the stopped tracee, delivering any pending signal.
369
384
pub fn restart ( & mut self , tracee : Tracee , restart : Restart ) -> Result < ( ) > {
370
385
let Tracee { pid, pending, .. } = tracee;
@@ -417,21 +432,58 @@ impl Ptracer {
417
432
r
418
433
}
419
434
435
+ // Poll tracees for a `wait(2)` status change.
436
+ fn poll_tracees ( & self ) -> Result < Option < WaitStatus > > {
437
+ let flag = WaitPidFlag :: __WALL | WaitPidFlag :: WNOHANG ;
438
+
439
+ for tracee in self . tracees . keys ( ) . copied ( ) {
440
+ let pid = Pid :: from_raw ( tracee) ;
441
+
442
+ match wait:: waitpid ( pid, Some ( flag) ) {
443
+ Ok ( WaitStatus :: StillAlive ) => {
444
+ // Alive, no state change. Check remaining tracees.
445
+ continue ;
446
+ } ,
447
+ Ok ( status) => {
448
+ // One of our tracees changed state.
449
+ return Ok ( Some ( status) ) ;
450
+ } ,
451
+ Err ( errno) if errno == Errno :: ECHILD => {
452
+ // No more children to wait on: we're done.
453
+ return Ok ( None )
454
+ } ,
455
+ Err ( err) => {
456
+ // Something else went wrong.
457
+ return Err ( err. into ( ) )
458
+ } ,
459
+ } ;
460
+ }
461
+
462
+ // No tracee changed state.
463
+ Ok ( None )
464
+ }
465
+
420
466
/// Wait for some running tracee process to stop.
421
467
///
422
468
/// If there are no tracees to wait on, returns `None`.
423
469
pub fn wait ( & mut self ) -> Result < Option < Tracee > > {
424
470
use Signal :: * ;
425
471
426
- let status = match wait:: waitpid ( None , WALL ) {
427
- Ok ( status) =>
428
- status,
429
- Err ( errno) if errno == Errno :: ECHILD =>
430
- // No more children to wait on: we're done.
431
- return Ok ( None ) ,
432
- Err ( err) =>
433
- return Err ( err. into ( ) ) ,
434
- } ;
472
+ let status;
473
+
474
+ loop {
475
+ if self . tracees . is_empty ( ) {
476
+ return Ok ( None ) ;
477
+ }
478
+
479
+ if let Some ( new_status) = self . poll_tracees ( ) ? {
480
+ // A tracee changed state, examine its `wait(2)` status.
481
+ status = new_status;
482
+ break ;
483
+ } else {
484
+ std:: thread:: sleep ( self . poll_delay ) ;
485
+ }
486
+ }
435
487
436
488
let tracee = match status {
437
489
WaitStatus :: Exited ( pid, _exit_code) => {
0 commit comments