# analyze_default_signal_behavior.pl: see what each signal does # to a Perl program when the "DEFAULT" signal handler is set on # that program. The results can be appended to the # lib/Signals/XSIG/Default.pm file. use IO::Handle; use POSIX ':sys_wait_h'; use Config; use strict; use warnings; $| = 1; if (@ARGV < 2) { print STDERR qq[ This program will experimentally determine the default behavior of each signal on your system. The data collected will be helpful in creating an appropriate Signals/XSIG/Default.pm file. ]; } my (@IGNORE, @SUSPEND, @TERMINATE, @UNKNOWN); my $num_simultaneous = shift @ARGV || 10; @IGNORE = ('__WARN__','__DIE__'); my @sigs = (sort keys %SIG, 'ZERO'); @sigs = @ARGV if @ARGV > 0; my $abort_status; if (@ARGV < 2) { printf STDERR "There are %d signals to analyze.\n", scalar @sigs; print STDERR "This may take a few minutes.\n\n"; } # figure out the exit status of a program that calls POSIX::abort() sub abort_status { use POSIX (); if (!defined $abort_status) { if (fork() == 0) { POSIX::abort(); exit 0; } wait; $abort_status = $? || -9E9; } return $abort_status; } sub analysis_file { my ($signal) = @_; "siganal$signal.txt"; } sub analyze_default_behavior_for_signal { my ($sig, $i, $analysis_file, $analysis_script) = @_; $analysis_file ||= analysis_file($sig); $analysis_script ||= "siganal$i.pl"; unlink $analysis_file, $analysis_script; open my $cfh, '>', $analysis_script; print $cfh qq[ \$SIG{'$sig'} = 'DEFAULT'; my \$n = sleep 2; my \$msg = "CHILD \$n / 4\n"; open(F, '>>', '$analysis_file'); print F \$msg; close F; exit 0; ]; close $cfh; my $status = 'unknown'; my ($pid, $win32ProcObj); if ($^O eq 'MSWin32') { require Win32::Process; require Win32; Win32::Process::Create( $win32ProcObj, $^X, "$^X $analysis_script", 0, &Win32::Process::NORMAL_PRIORITY_CLASS, "." ) || die Win32::GetLastError(); $pid = $win32ProcObj->GetProcessID(); } else { $pid = fork(); if ($pid == 0) { %SIG = (); exec $^X, $analysis_script; die; } } sleep 2; my $nk = kill $sig, $pid; sleep 1; my $r = waitpid $pid, &WNOHANG; $status = $? if $r == $pid; sleep 5; $nk = kill 'CONT', $pid; sleep 1; $r = waitpid $pid, &WNOHANG; $status = $? if $r == $pid; $nk = kill 'KILL', $pid; $r = waitpid $pid, 0; $status = $? if $r == $pid; if (defined $win32ProcObj) { $win32ProcObj->Wait(&Win32::Process::INFINITE); } my $msg = "Status: $status\n"; open my $fh, '>>', $analysis_file; print $fh $msg; close $fh; unlink $analysis_script; return $analysis_file; } $::j=0; print "[$^O]\n"; while (@sigs) { my @sigz = splice @sigs, 0, $num_simultaneous; my $i = 0; foreach my $sig (@sigz) { if (fork() == 0) { analyze_default_behavior_for_signal($sig,$i); exit 0; } $i++; } wait foreach @sigz; foreach my $sig (@sigz) { parse_analysis_file($sig,analysis_file($sig)); } } sub parse_analysis_file { my ($sig,$file) = @_; open G, '<', $file; my @g = ; close G; my $i = ++$::j; my @sig_name = split ' ', $Config{sig_name}; my @sig_num = split ' ', $Config{sig_num}; my ($sig_no) = grep { $sig_name[$_] eq $sig } 0..$#sig_num; $sig_no ||= 9999; $sig_no = $sig_num[$sig_no]; $sig_no ||= ''; my ($sleep_result, $sleep_benchmark) = $g[0] =~ /CHILD (\d+) \/ (\d+)/; if (defined($sleep_benchmark) && $sleep_result > $sleep_benchmark) { # the program completed but took longer than ~4 seconds. # This means it was suspended and then resumed several seconds later. push @SUSPEND, $sig; printf STDERR "%d. SIG", $i; printf "%-7s [%s] => %s\n", $sig, $sig_no, "SUSPEND"; } else { my ($status) = $g[-1] =~ /Status: (\d+)/; if ($status eq "0") { # The program completed normally in a regular amount of time. # The signal was ignored or not received. push @IGNORE, $sig; printf STDERR "%d. SIG", $i; printf "%-7s [%s] => %s\n", $sig, $sig_no, "IGNORE"; } elsif ($status > 0) { # The program did not complete normally. # The signal terminated the program. push @TERMINATE, $sig; printf STDERR "%d. SIG", $i; if ($status == $sig_no << 8) { # exit status is divisible by 256. Like quitting with exit() printf "%-7s [%s] => %s\n", $sig, $sig_no, "EXIT $sig_no"; } elsif (0 && $status == abort_status()) { # exit status same as abort status (see &abort_status). printf "%-7s [%s] => %s\n", $sig, $sig_no, "ABORT"; } else { # exit status not divisible by 256. Only way to do this # reliably is to actually raise the signal printf "%-7s [%s] => %s\n", $sig, $sig_no, "TERMINATE $status"; } } else { push @UNKNOWN, $sig; printf STDERR "%d. SIG", $i; printf "%-7s [%s] => %d %s\n", $sig, $sig_no, $status, "UNKNOWN"; } } unlink $file; }