15759b3d2Safresh1#!/usr/bin/perl 25759b3d2Safresh1# binary search maximum stack depth for arrays and hashes 3b46d8ef2Safresh1# and report it to stdout as code to set the limits 45759b3d2Safresh1 55759b3d2Safresh1use Config; 65759b3d2Safresh1use Cwd; 75759b3d2Safresh1use File::Spec; 85759b3d2Safresh1use strict; 95759b3d2Safresh1 105759b3d2Safresh1my $ptrsize = $Config{ptrsize}; 115759b3d2Safresh1my ($bad1, $bad2) = (65001, 25000); 125759b3d2Safresh1sub QUIET () { 135759b3d2Safresh1 (defined $ENV{MAKEFLAGS} and $ENV{MAKEFLAGS} =~ /\b(s|silent|quiet)\b/ 14b46d8ef2Safresh1 and !defined($ENV{TRAVIS})) || @ARGV && $ARGV[0] eq "-q" 155759b3d2Safresh1 ? 1 : 0 165759b3d2Safresh1} 175759b3d2Safresh1sub PARALLEL () { 185759b3d2Safresh1 if (defined $ENV{MAKEFLAGS} 195759b3d2Safresh1 and $ENV{MAKEFLAGS} =~ /\bj\s*(\d+)\b/ 205759b3d2Safresh1 and $1 > 1) { 215759b3d2Safresh1 return 1; 225759b3d2Safresh1 } else { 235759b3d2Safresh1 return 0; 245759b3d2Safresh1 } 255759b3d2Safresh1} 265759b3d2Safresh1sub is_miniperl { 275759b3d2Safresh1 return !defined &DynaLoader::boot_DynaLoader; 285759b3d2Safresh1} 295759b3d2Safresh1 305759b3d2Safresh1if (is_miniperl()) { 31b46d8ef2Safresh1 die "Should not run using miniperl\n"; 325759b3d2Safresh1} 335759b3d2Safresh1my $prefix = ""; 345759b3d2Safresh1if ($^O eq "MSWin32") { 355759b3d2Safresh1 # prevent Windows popping up a dialog each time we overflow 365759b3d2Safresh1 # the stack 375759b3d2Safresh1 require Win32API::File; 385759b3d2Safresh1 Win32API::File->import(qw(SetErrorMode SEM_NOGPFAULTERRORBOX SEM_FAILCRITICALERRORS)); 395759b3d2Safresh1 SetErrorMode(SEM_NOGPFAULTERRORBOX() | SEM_FAILCRITICALERRORS()); 405759b3d2Safresh1} 415759b3d2Safresh1# the ; here is to ensure system() passes this to the shell 425759b3d2Safresh1elsif (system("ulimit -c 0 ;") == 0) { 435759b3d2Safresh1 # try to prevent core dumps 445759b3d2Safresh1 $prefix = "ulimit -c 0 ; "; 455759b3d2Safresh1} 465759b3d2Safresh1my $PERL = $^X; 475759b3d2Safresh1if ($^O eq "MSWin32") { 485759b3d2Safresh1 require Win32; 495759b3d2Safresh1 my ($str, $major, $minor) = Win32::GetOSVersion(); 505759b3d2Safresh1 if ($major < 6 || $major == 6 && $minor < 1) { 51b46d8ef2Safresh1 print "# Using defaults for older Win32\n"; 525759b3d2Safresh1 write_limits(500, 256); 535759b3d2Safresh1 exit; 545759b3d2Safresh1 } 555759b3d2Safresh1} 565759b3d2Safresh1my ($n, $good, $bad, $found) = 575759b3d2Safresh1 (65000, 100, $bad1, undef); 58b46d8ef2Safresh1print "# probe for max. stack sizes...\n" unless QUIET; 595759b3d2Safresh1# -I. since we're run before pm_to_blib (which is going to copy the 605759b3d2Safresh1# file we create) and need to load our Storable.pm, not the already 615759b3d2Safresh1# installed Storable.pm 62b46d8ef2Safresh1my $mblib = ''; 63b46d8ef2Safresh1if (-d 'blib') { 64b46d8ef2Safresh1 $mblib = '-Mblib -I.'; 655759b3d2Safresh1} 66b46d8ef2Safresh1elsif (-f "Configure") { 67b46d8ef2Safresh1 $mblib = '-Ilib'; 685759b3d2Safresh1} 695759b3d2Safresh1 705759b3d2Safresh1sub cmd { 715759b3d2Safresh1 my ($i, $try, $limit_name) = @_; 725759b3d2Safresh1 die unless $i; 735759b3d2Safresh1 my $code = "my \$t; \$Storable::$limit_name = -1; $try for 1..$i;dclone(\$t); print qq/ok\n/"; 745759b3d2Safresh1 my $q = ($^O eq 'MSWin32') ? '"' : "'"; 755759b3d2Safresh1 765759b3d2Safresh1 "$prefix $PERL $mblib -MStorable=dclone -e$q$code$q" 775759b3d2Safresh1} 785759b3d2Safresh1# try more 795759b3d2Safresh1sub good { 805759b3d2Safresh1 my $i = shift; # this passed 815759b3d2Safresh1 my $j = $i + abs(int(($bad - $i) / 2)); 82b46d8ef2Safresh1 print "# Storable: determining recursion limit: $i passed, try more $j ...\n" unless QUIET; 835759b3d2Safresh1 $good = $i; 845759b3d2Safresh1 if ($j <= $i) { 855759b3d2Safresh1 $found++; 865759b3d2Safresh1 } 875759b3d2Safresh1 return $j; 885759b3d2Safresh1} 895759b3d2Safresh1# try less 905759b3d2Safresh1sub bad { 915759b3d2Safresh1 my $i = shift; # this failed 925759b3d2Safresh1 my $j = $i - abs(int(($i - $good) / 2)); 93b46d8ef2Safresh1 print "# Storable: determining recursion limit: $i too big, try less $j ...\n" unless QUIET; 945759b3d2Safresh1 $bad = $i; 955759b3d2Safresh1 if ($j >= $i) { 965759b3d2Safresh1 $j = $good; 975759b3d2Safresh1 $found++; 985759b3d2Safresh1 } 995759b3d2Safresh1 return $j; 1005759b3d2Safresh1} 1015759b3d2Safresh1 1025759b3d2Safresh1sub array_cmd { 1035759b3d2Safresh1 my $depth = shift; 1045759b3d2Safresh1 return cmd($depth, '$t=[$t]', 'recursion_limit'); 1055759b3d2Safresh1} 1065759b3d2Safresh1 1075759b3d2Safresh1# first check we can successfully run with a minimum level 1085759b3d2Safresh1my $cmd = array_cmd(1); 1095759b3d2Safresh1unless ((my $output = `$cmd`) =~ /\bok\b/) { 1105759b3d2Safresh1 die "Cannot run probe: '$output', aborting...\n"; 1115759b3d2Safresh1} 1125759b3d2Safresh1 1135759b3d2Safresh1unless ($ENV{STORABLE_NOISY}) { 1145759b3d2Safresh1 # suppress Segmentation fault messages 1155759b3d2Safresh1 open STDERR, ">", File::Spec->devnull; 1165759b3d2Safresh1} 1175759b3d2Safresh1 1185759b3d2Safresh1while (!$found) { 1195759b3d2Safresh1 my $cmd = array_cmd($n); 1205759b3d2Safresh1 #print "$cmd\n" unless $QUIET; 1215759b3d2Safresh1 if (`$cmd` =~ /\bok\b/) { 1225759b3d2Safresh1 $n = good($n); 1235759b3d2Safresh1 } else { 1245759b3d2Safresh1 $n = bad($n); 1255759b3d2Safresh1 } 1265759b3d2Safresh1} 127b46d8ef2Safresh1print "# MAX_DEPTH = $n\n" unless QUIET; 1285759b3d2Safresh1my $max_depth = $n; 1295759b3d2Safresh1 1305759b3d2Safresh1($n, $good, $bad, $found) = 1315759b3d2Safresh1 (int($n/2), 50, $n, undef); 1325759b3d2Safresh1# pack j only since 5.8 1335759b3d2Safresh1my $max = ($] > 5.007 and length(pack "j", 0) < 8) 1345759b3d2Safresh1 ? ($^O eq 'MSWin32' ? 3000 : 8000) 1355759b3d2Safresh1 : $max_depth; 1365759b3d2Safresh1$n = $max if $n > $max; 1375759b3d2Safresh1$bad = $max if $bad > $max; 1385759b3d2Safresh1while (!$found) { 1395759b3d2Safresh1 my $cmd = cmd($n, '$t={1=>$t}', 'recursion_limit_hash'); 1405759b3d2Safresh1 #print "$cmd\n" unless $QUIET; 1415759b3d2Safresh1 if (`$cmd` =~ /\bok\b/) { 1425759b3d2Safresh1 $n = good($n); 1435759b3d2Safresh1 } else { 1445759b3d2Safresh1 $n = bad($n); 1455759b3d2Safresh1 } 1465759b3d2Safresh1} 1475759b3d2Safresh1if ($max_depth == $bad1-1 1485759b3d2Safresh1 and $n == $bad2-1) 1495759b3d2Safresh1{ 1505759b3d2Safresh1 # more likely the shell. travis docker ubuntu, mingw e.g. 151b46d8ef2Safresh1 print "# Apparently your system(SHELLSTRING) cannot catch stack overflows\n" 1525759b3d2Safresh1 unless QUIET; 1535759b3d2Safresh1 $max_depth = 512; 1545759b3d2Safresh1 $n = 256; 1555759b3d2Safresh1 print "MAX_DEPTH = $max_depth\n" unless QUIET; 1565759b3d2Safresh1} 157b46d8ef2Safresh1print "# MAX_DEPTH_HASH = $n\n" unless QUIET; 1585759b3d2Safresh1my $max_depth_hash = $n; 1595759b3d2Safresh1 1605759b3d2Safresh1# Previously this calculation was done in the macro, calculate it here 1615759b3d2Safresh1# instead so a user setting of either variable more closely matches 1625759b3d2Safresh1# the limits the use sees. 1635759b3d2Safresh1 164*56d68f1eSafresh1# be fairly aggressive in trimming this, smoke testing showed 1655759b3d2Safresh1# several apparently random failures here, eg. working in one 1665759b3d2Safresh1# configuration, but not in a very similar configuration. 1675759b3d2Safresh1$max_depth = int(0.6 * $max_depth); 168b46d8ef2Safresh1$max_depth_hash = int(0.6 * $max_depth_hash); 1695759b3d2Safresh1 1705759b3d2Safresh1my $stack_reserve = $^O eq "MSWin32" ? 32 : 16; 1715759b3d2Safresh1if ($] ge "5.016" && !($^O eq "cygwin" && $ptrsize == 8)) { 1725759b3d2Safresh1 $max_depth -= $stack_reserve; 1735759b3d2Safresh1 $max_depth_hash -= $stack_reserve; 1745759b3d2Safresh1} 1755759b3d2Safresh1else { 1765759b3d2Safresh1 # within the exception we need another stack depth to recursively 1775759b3d2Safresh1 # cleanup the hash 1785759b3d2Safresh1 $max_depth = ($max_depth >> 1) - $stack_reserve; 1795759b3d2Safresh1 $max_depth_hash = ($max_depth_hash >> 1) - $stack_reserve * 2; 1805759b3d2Safresh1} 1815759b3d2Safresh1 1825759b3d2Safresh1write_limits($max_depth, $max_depth_hash); 1835759b3d2Safresh1 1845759b3d2Safresh1sub write_limits { 1855759b3d2Safresh1 my ($max_depth, $max_depth_hash) = @_; 186b46d8ef2Safresh1 print <<EOS; 1875759b3d2Safresh1# bisected by stacksize 1885759b3d2Safresh1\$Storable::recursion_limit = $max_depth 1895759b3d2Safresh1 unless defined \$Storable::recursion_limit; 1905759b3d2Safresh1\$Storable::recursion_limit_hash = $max_depth_hash 1915759b3d2Safresh1 unless defined \$Storable::recursion_limit_hash; 1925759b3d2Safresh1EOS 1935759b3d2Safresh1} 194