#!/usr/bin/perl
# binary search maximum stack depth for arrays and hashes
-# and store it in stacksize.h
+# and report it to stdout as code to set the limits
use Config;
use Cwd;
+use File::Spec;
+use strict;
-my $f;
-my $fn = "stacksize.h";
-my ($bad1, $bad2) = (45000, 25000);
+my $ptrsize = $Config{ptrsize};
+my ($bad1, $bad2) = (65001, 25000);
sub QUIET () {
(defined $ENV{MAKEFLAGS} and $ENV{MAKEFLAGS} =~ /\b(s|silent|quiet)\b/
- and !defined($ENV{TRAVIS}))
+ and !defined($ENV{TRAVIS})) || @ARGV && $ARGV[0] eq "-q"
? 1 : 0
}
sub PARALLEL () {
}
if (is_miniperl()) {
- print "skip miniperl\n" unless QUIET;
- exit;
+ die "Should not run using miniperl\n";
}
-if (@ARGV and $ARGV[0] eq '--core') {
- $ENV{PERL_CORE} = 1;
+my $prefix = "";
+if ($^O eq "MSWin32") {
+ # prevent Windows popping up a dialog each time we overflow
+ # the stack
+ require Win32API::File;
+ Win32API::File->import(qw(SetErrorMode SEM_NOGPFAULTERRORBOX SEM_FAILCRITICALERRORS));
+ SetErrorMode(SEM_NOGPFAULTERRORBOX() | SEM_FAILCRITICALERRORS());
}
-my $PERL = $^X;
-if ($ENV{PERL_CORE}) {
- my $path;
- my $ldlib = $Config{ldlibpthname};
- if (-d 'dist/Storable') {
- chdir 'dist/Storable';
- $PERL = "../../$PERL" unless $PERL =~ m|^/|;
- }
- if ($ldlib) {
- $path = getcwd()."/../..";
- }
- if ($^O eq 'MSWin32' and -d '../dist/Storable') {
- chdir '..\dist\Storable';
- $PERL = "..\\..\\$PERL" unless $PERL =~ /^[A-Za-z]:\\/;
- }
- $PERL = "\"$PERL\"" if $PERL =~ / /;
- if ($ldlib and $ldlib ne 'PATH') {
- $PERL = "$ldlib=$path $PERL";
- }
+# the ; here is to ensure system() passes this to the shell
+elsif (system("ulimit -c 0 ;") == 0) {
+ # try to prevent core dumps
+ $prefix = "ulimit -c 0 ; ";
}
-
-if (open $f, "<", $fn) {
- my $s;
- while ($s = <$f>) {
- #print $s unless $QUIET;
- if ($s =~ m|^/\* bisected|) {
- print "already bisected\n" unless QUIET;
- exit;
- }
+my $PERL = $^X;
+if ($^O eq "MSWin32") {
+ require Win32;
+ my ($str, $major, $minor) = Win32::GetOSVersion();
+ if ($major < 6 || $major == 6 && $minor < 1) {
+ print "# Using defaults for older Win32\n";
+ write_limits(500, 256);
+ exit;
}
- close $f;
}
-
my ($n, $good, $bad, $found) =
- (35000, 100, $bad1, undef);
-my $try = '$t=[$t]';
-print "probe for max. stack sizes...\n" unless QUIET;
-my $mblib = '-Mblib';
-if ($ENV{PERL_CORE}) {
- if ($^O eq 'MSWin32') {
- $mblib = '-I..\..\lib\auto -I..\..\lib';
- } else {
- $mblib = '-I../../lib/auto -I../../lib';
- }
-}
-if (PARALLEL) {
- # problem with parallel builds. wait for INST_DYNAMIC linking to be done.
- # the problem is the RM_F INST_DYNAMIC race.
- print "parallel build race - wait for linker ...\n" unless QUIET;
- sleep(2.0);
-}
-for my $i (0..1) {
- my $probe ="$PERL $mblib -e\"require Storable or die; Storable::dclone([]) or die;\"";
- if (system($probe) != 0) {
- print "Storable not yet usable: $probe\n" unless QUIET;
- unless ($i) { # XXX race?
- system($Config{make});
- # and try again
- } else {
- exit;
- }
- } else {
- last;
- }
+ (65000, 100, $bad1, undef);
+print "# probe for max. stack sizes...\n" unless QUIET;
+# -I. since we're run before pm_to_blib (which is going to copy the
+# file we create) and need to load our Storable.pm, not the already
+# installed Storable.pm
+my $mblib = '';
+if (-d 'blib') {
+ $mblib = '-Mblib -I.';
+}
+elsif (-f "Configure") {
+ $mblib = '-Ilib';
}
sub cmd {
- my $i = shift;
+ my ($i, $try, $limit_name) = @_;
die unless $i;
- ($^O eq 'MSWin32')
- ? "$PERL $mblib -MStorable=dclone -e\"my \$t; $try for 1..$i;dclone(\$t);\""
- : "$PERL $mblib -MStorable=dclone -e'my \$t; $try for 1..$i;dclone(\$t);'"
+ my $code = "my \$t; \$Storable::$limit_name = -1; $try for 1..$i;dclone(\$t); print qq/ok\n/";
+ my $q = ($^O eq 'MSWin32') ? '"' : "'";
+
+ "$prefix $PERL $mblib -MStorable=dclone -e$q$code$q"
}
# try more
sub good {
my $i = shift; # this passed
my $j = $i + abs(int(($bad - $i) / 2));
- print "$i passed, try more $j ...\n" unless QUIET;
+ print "# Storable: determining recursion limit: $i passed, try more $j ...\n" unless QUIET;
$good = $i;
if ($j <= $i) {
$found++;
sub bad {
my $i = shift; # this failed
my $j = $i - abs(int(($i - $good) / 2));
- print "$i failed, try less $j ...\n" unless QUIET;
+ print "# Storable: determining recursion limit: $i too big, try less $j ...\n" unless QUIET;
$bad = $i;
if ($j >= $i) {
$j = $good;
return $j;
}
+sub array_cmd {
+ my $depth = shift;
+ return cmd($depth, '$t=[$t]', 'recursion_limit');
+}
+
+# first check we can successfully run with a minimum level
+my $cmd = array_cmd(1);
+unless ((my $output = `$cmd`) =~ /\bok\b/) {
+ die "Cannot run probe: '$output', aborting...\n";
+}
+
+unless ($ENV{STORABLE_NOISY}) {
+ # suppress Segmentation fault messages
+ open STDERR, ">", File::Spec->devnull;
+}
+
while (!$found) {
- my $cmd = cmd($n);
+ my $cmd = array_cmd($n);
#print "$cmd\n" unless $QUIET;
- if (system($cmd) == 0) {
+ if (`$cmd` =~ /\bok\b/) {
$n = good($n);
} else {
$n = bad($n);
}
}
-print "MAX_DEPTH = $n\n" unless QUIET;
+print "# MAX_DEPTH = $n\n" unless QUIET;
my $max_depth = $n;
($n, $good, $bad, $found) =
# pack j only since 5.8
my $max = ($] > 5.007 and length(pack "j", 0) < 8)
? ($^O eq 'MSWin32' ? 3000 : 8000)
- : $bad2;
+ : $max_depth;
$n = $max if $n > $max;
$bad = $max if $bad > $max;
-$try = '$t={1=>$t}';
while (!$found) {
- my $cmd = cmd($n);
+ my $cmd = cmd($n, '$t={1=>$t}', 'recursion_limit_hash');
#print "$cmd\n" unless $QUIET;
- if (system($cmd) == 0) {
+ if (`$cmd` =~ /\bok\b/) {
$n = good($n);
} else {
$n = bad($n);
if ($max_depth == $bad1-1
and $n == $bad2-1)
{
- # more likely the shell. travis docker ubuntu e.g.
- print "Error: Apparently your system(SHELLSTRING) cannot catch stack overflows\n"
+ # more likely the shell. travis docker ubuntu, mingw e.g.
+ print "# Apparently your system(SHELLSTRING) cannot catch stack overflows\n"
unless QUIET;
$max_depth = 512;
$n = 256;
print "MAX_DEPTH = $max_depth\n" unless QUIET;
}
-print "MAX_DEPTH_HASH = $n\n" unless QUIET;
+print "# MAX_DEPTH_HASH = $n\n" unless QUIET;
my $max_depth_hash = $n;
-open $f, ">", $fn or exit(1);
-print $f "/* bisected with stacksize.pl */\n";
-print $f "#define PST_STACK_MAX_DEPTH $max_depth\n";
-print $f "#define PST_STACK_MAX_DEPTH_HASH $max_depth_hash\n";
-close $f;
+# Previously this calculation was done in the macro, calculate it here
+# instead so a user setting of either variable more closely matches
+# the limits the use sees.
+
+# be fairly aggressive in trimming this, smoke testing showed several
+# several apparently random failures here, eg. working in one
+# configuration, but not in a very similar configuration.
+$max_depth = int(0.6 * $max_depth);
+$max_depth_hash = int(0.6 * $max_depth_hash);
+
+my $stack_reserve = $^O eq "MSWin32" ? 32 : 16;
+if ($] ge "5.016" && !($^O eq "cygwin" && $ptrsize == 8)) {
+ $max_depth -= $stack_reserve;
+ $max_depth_hash -= $stack_reserve;
+}
+else {
+ # within the exception we need another stack depth to recursively
+ # cleanup the hash
+ $max_depth = ($max_depth >> 1) - $stack_reserve;
+ $max_depth_hash = ($max_depth_hash >> 1) - $stack_reserve * 2;
+}
+
+write_limits($max_depth, $max_depth_hash);
+
+sub write_limits {
+ my ($max_depth, $max_depth_hash) = @_;
+ print <<EOS;
+# bisected by stacksize
+\$Storable::recursion_limit = $max_depth
+ unless defined \$Storable::recursion_limit;
+\$Storable::recursion_limit_hash = $max_depth_hash
+ unless defined \$Storable::recursion_limit_hash;
+EOS
+}