diff options
author | Nicholas Clark <nick@ccl4.org> | 2012-07-31 18:54:24 +0200 |
---|---|---|
committer | Nicholas Clark <nick@ccl4.org> | 2013-05-28 09:19:29 +0200 |
commit | 1fdd0dcc48757998b9a4aea3f011e35a5bfb262a (patch) | |
tree | 91fb08de0e8d884ee65b9b0b6cea49672d14e9d4 /Porting/bisect-runner.pl | |
parent | b9dcd8de22bd198ab6c928d76949419e8c24c131 (diff) | |
download | perl-1fdd0dcc48757998b9a4aea3f011e35a5bfb262a.tar.gz |
bisect.pl can now optionally timeout the user's test case.
This permits bisection to locate the cause (or cure) of bugs which cause
programs to hang. When using a timeout, bisect-runner.pl defaults to
running the test case in its own process group, and tries hard to ensure
that all processes in that process group are killed if the timeout fires.
Diffstat (limited to 'Porting/bisect-runner.pl')
-rwxr-xr-x | Porting/bisect-runner.pl | 63 |
1 files changed, 57 insertions, 6 deletions
diff --git a/Porting/bisect-runner.pl b/Porting/bisect-runner.pl index 110d67c72f..83d0a87c24 100755 --- a/Porting/bisect-runner.pl +++ b/Porting/bisect-runner.pl @@ -60,7 +60,7 @@ unless(GetOptions(\%options, $options{match} = $_[1]; $options{'expect-pass'} = 0; }, - 'force-manifest', 'force-regen', 'setpgrp!', + 'force-manifest', 'force-regen', 'setpgrp!', 'timeout=i', 'test-build', 'validate', 'all-fixups', 'early-fixup=s@', 'late-fixup=s@', 'valgrind', 'check-args', 'check-shebang!', 'usage|help|?', 'gold=s', @@ -488,10 +488,18 @@ C<--expect-pass=0> is equivalent to C<--expect-fail>. I<1> is the default. =item * +--timeout I<seconds> + +Run the testcase with the given timeout. If this is exceeded, kill it (and +by default all its children), and treat it as a failure. + +=item * + --setpgrp Run the testcase in its own process group. Specifically, call C<setpgrp 0, 0> -just before C<exec>-ing the user testcase. +just before C<exec>-ing the user testcase. The default is not to set the +process group, unless a timeout is used. =item * @@ -724,22 +732,63 @@ sub run_with_options { my $name = $options->{name}; $name = "@_" unless defined $name; + my $setgrp = $options->{setpgrp}; + if ($options->{timeout}) { + # Unless you explicitly disabled it on the commandline, set it: + $setgrp = 1 unless defined $setgrp; + } my $pid = fork; die_255("Can't fork: $!") unless defined $pid; if (!$pid) { if (exists $options->{stdin}) { open STDIN, '<', $options->{stdin} - or die "Can't open STDIN from $options->{stdin}: $!"; + or die "Can't open STDIN from $options->{stdin}: $!"; } - if ($options->{setpgrp}) { + if ($setgrp) { setpgrp 0, 0 or die "Can't setpgrp 0, 0: $!"; } { exec @_ }; die_255("Failed to start $name: $!"); } + my $start; + if ($options->{timeout}) { + require Errno; + require POSIX; + die_255("No POSIX::WNOHANG") + unless &POSIX::WNOHANG; + $start = time; + $SIG{ALRM} = sub { + my $victim = $setgrp ? -$pid : $pid; + my $delay = 1; + kill 'TERM', $victim; + waitpid(-1, &POSIX::WNOHANG); + while (kill 0, $victim) { + sleep $delay; + waitpid(-1, &POSIX::WNOHANG); + $delay *= 2; + if ($delay > 8) { + if (kill 'KILL', $victim) { + print STDERR "$0: Had to kill 'KILL', $victim\n" + } elsif (! $!{ESRCH}) { + print STDERR "$0: kill 'KILL', $victim failed: $!\n"; + } + last; + } + } + report_and_exit(0, 'No timeout', 'Timeout', "when running $name"); + }; + alarm $options->{timeout}; + } waitpid $pid, 0 - or die_255("wait for $name, pid $pid failed: $!"); + or die_255("wait for $name, pid $pid failed: $!"); + alarm 0; + if ($options->{timeout}) { + my $elapsed = time - $start; + if ($elapsed / $options->{timeout} > 0.8) { + print STDERR "$0: Beware, took $elapsed seconds of $options->{timeout} permitted to run $name\n"; + } + } return $?; } @@ -1015,7 +1064,9 @@ sub report_and_exit { } sub run_report_and_exit { - my $ret = run_with_options({setprgp => $options{setpgrp}}, @_); + my $ret = run_with_options({setprgp => $options{setpgrp}, + timeout => $options{timeout}, + }, @_); report_and_exit(!$ret, 'zero exit from', 'non-zero exit from', "@_"); } |