1#!/usr/bin/perl 2# binary search maximum stack depth for arrays and hashes 3# and store it in lib/Storable/Limit.pm 4 5use Config; 6use Cwd; 7use File::Spec; 8use strict; 9 10my $fn = "lib/Storable/Limit.pm"; 11my $ptrsize = $Config{ptrsize}; 12my ($bad1, $bad2) = (65001, 25000); 13sub QUIET () { 14 (defined $ENV{MAKEFLAGS} and $ENV{MAKEFLAGS} =~ /\b(s|silent|quiet)\b/ 15 and !defined($ENV{TRAVIS})) 16 ? 1 : 0 17} 18sub PARALLEL () { 19 if (defined $ENV{MAKEFLAGS} 20 and $ENV{MAKEFLAGS} =~ /\bj\s*(\d+)\b/ 21 and $1 > 1) { 22 return 1; 23 } else { 24 return 0; 25 } 26} 27sub is_miniperl { 28 return !defined &DynaLoader::boot_DynaLoader; 29} 30 31if (is_miniperl()) { 32 die "Should not run during miniperl\n"; 33} 34my $prefix = ""; 35if ($^O eq "MSWin32") { 36 # prevent Windows popping up a dialog each time we overflow 37 # the stack 38 require Win32API::File; 39 Win32API::File->import(qw(SetErrorMode SEM_NOGPFAULTERRORBOX SEM_FAILCRITICALERRORS)); 40 SetErrorMode(SEM_NOGPFAULTERRORBOX() | SEM_FAILCRITICALERRORS()); 41} 42# the ; here is to ensure system() passes this to the shell 43elsif (system("ulimit -c 0 ;") == 0) { 44 # try to prevent core dumps 45 $prefix = "ulimit -c 0 ; "; 46} 47if (@ARGV and $ARGV[0] eq '--core') { 48 $ENV{PERL_CORE} = 1; 49} 50my $PERL = $^X; 51if ($ENV{PERL_CORE}) { 52 my $path; 53 my $ldlib = $Config{ldlibpthname}; 54 if (-d 'dist/Storable') { 55 chdir 'dist/Storable'; 56 $PERL = "../../$PERL" unless $PERL =~ m|^/|; 57 } 58 if ($ldlib) { 59 $path = getcwd()."/../.."; 60 } 61 if ($^O eq 'MSWin32' and -d '../dist/Storable') { 62 chdir '..\dist\Storable'; 63 $PERL = "..\\..\\$PERL" unless $PERL =~ /^[A-Za-z]:\\/; 64 } 65 $PERL = "\"$PERL\"" if $PERL =~ / /; 66 if ($ldlib and $ldlib ne 'PATH') { 67 $PERL = "$ldlib=$path $PERL"; 68 } 69} 70 71-d "lib" or mkdir "lib"; 72-d "lib/Storable" or mkdir "lib/Storable"; 73 74if ($^O eq "MSWin32") { 75 require Win32; 76 my ($str, $major, $minor) = Win32::GetOSVersion(); 77 if ($major < 6 || $major == 6 && $minor < 1) { 78 print "Using defaults for older Win32\n"; 79 write_limits(500, 256); 80 exit; 81 } 82} 83my ($n, $good, $bad, $found) = 84 (65000, 100, $bad1, undef); 85print "probe for max. stack sizes...\n" unless QUIET; 86# -I. since we're run before pm_to_blib (which is going to copy the 87# file we create) and need to load our Storable.pm, not the already 88# installed Storable.pm 89my $mblib = '-Mblib -I.'; 90if ($ENV{PERL_CORE}) { 91 if ($^O eq 'MSWin32') { 92 $mblib = '-I..\..\lib\auto -I..\..\lib'; 93 } else { 94 $mblib = '-I../../lib/auto -I../../lib'; 95 } 96} 97if (PARALLEL) { 98 # problem with parallel builds. wait for INST_DYNAMIC linking to be done. 99 # the problem is the RM_F INST_DYNAMIC race. 100 print "parallel build race - wait for linker ...\n" unless QUIET; 101 sleep(2.0); 102} 103 104sub cmd { 105 my ($i, $try, $limit_name) = @_; 106 die unless $i; 107 my $code = "my \$t; \$Storable::$limit_name = -1; $try for 1..$i;dclone(\$t); print qq/ok\n/"; 108 my $q = ($^O eq 'MSWin32') ? '"' : "'"; 109 110 "$prefix $PERL $mblib -MStorable=dclone -e$q$code$q" 111} 112# try more 113sub good { 114 my $i = shift; # this passed 115 my $j = $i + abs(int(($bad - $i) / 2)); 116 print "Storable: determining recursion limit: $i passed, try more $j ...\n" unless QUIET; 117 $good = $i; 118 if ($j <= $i) { 119 $found++; 120 } 121 return $j; 122} 123# try less 124sub bad { 125 my $i = shift; # this failed 126 my $j = $i - abs(int(($i - $good) / 2)); 127 print "Storable: determining recursion limit: $i too big, try less $j ...\n" unless QUIET; 128 $bad = $i; 129 if ($j >= $i) { 130 $j = $good; 131 $found++; 132 } 133 return $j; 134} 135 136sub array_cmd { 137 my $depth = shift; 138 return cmd($depth, '$t=[$t]', 'recursion_limit'); 139} 140 141# first check we can successfully run with a minimum level 142my $cmd = array_cmd(1); 143unless ((my $output = `$cmd`) =~ /\bok\b/) { 144 die "Cannot run probe: '$output', aborting...\n"; 145} 146 147unless ($ENV{STORABLE_NOISY}) { 148 # suppress Segmentation fault messages 149 open STDERR, ">", File::Spec->devnull; 150} 151 152while (!$found) { 153 my $cmd = array_cmd($n); 154 #print "$cmd\n" unless $QUIET; 155 if (`$cmd` =~ /\bok\b/) { 156 $n = good($n); 157 } else { 158 $n = bad($n); 159 } 160} 161print "MAX_DEPTH = $n\n" unless QUIET; 162my $max_depth = $n; 163 164($n, $good, $bad, $found) = 165 (int($n/2), 50, $n, undef); 166# pack j only since 5.8 167my $max = ($] > 5.007 and length(pack "j", 0) < 8) 168 ? ($^O eq 'MSWin32' ? 3000 : 8000) 169 : $max_depth; 170$n = $max if $n > $max; 171$bad = $max if $bad > $max; 172while (!$found) { 173 my $cmd = cmd($n, '$t={1=>$t}', 'recursion_limit_hash'); 174 #print "$cmd\n" unless $QUIET; 175 if (`$cmd` =~ /\bok\b/) { 176 $n = good($n); 177 } else { 178 $n = bad($n); 179 } 180} 181if ($max_depth == $bad1-1 182 and $n == $bad2-1) 183{ 184 # more likely the shell. travis docker ubuntu, mingw e.g. 185 print "Error: Apparently your system(SHELLSTRING) cannot catch stack overflows\n" 186 unless QUIET; 187 $max_depth = 512; 188 $n = 256; 189 print "MAX_DEPTH = $max_depth\n" unless QUIET; 190} 191print "MAX_DEPTH_HASH = $n\n" unless QUIET; 192my $max_depth_hash = $n; 193 194# Previously this calculation was done in the macro, calculate it here 195# instead so a user setting of either variable more closely matches 196# the limits the use sees. 197 198# be fairly aggressive in trimming this, smoke testing showed several 199# several apparently random failures here, eg. working in one 200# configuration, but not in a very similar configuration. 201$max_depth = int(0.6 * $max_depth); 202$max_depth_hash = int(0.6 * $max_depth); 203 204my $stack_reserve = $^O eq "MSWin32" ? 32 : 16; 205if ($] ge "5.016" && !($^O eq "cygwin" && $ptrsize == 8)) { 206 $max_depth -= $stack_reserve; 207 $max_depth_hash -= $stack_reserve; 208} 209else { 210 # within the exception we need another stack depth to recursively 211 # cleanup the hash 212 $max_depth = ($max_depth >> 1) - $stack_reserve; 213 $max_depth_hash = ($max_depth_hash >> 1) - $stack_reserve * 2; 214} 215 216write_limits($max_depth, $max_depth_hash); 217 218sub write_limits { 219 my ($max_depth, $max_depth_hash) = @_; 220 my $f; 221 open $f, ">", $fn or die "$fn $!"; 222 print $f <<EOS; 223# bisected by stacksize 224\$Storable::recursion_limit = $max_depth 225 unless defined \$Storable::recursion_limit; 226\$Storable::recursion_limit_hash = $max_depth_hash 227 unless defined \$Storable::recursion_limit_hash; 2281; 229EOS 230 close $f 231 or die "Failed to close $fn: $!\n"; 232} 233