xref: /openbsd-src/gnu/usr.bin/perl/dist/Storable/stacksize (revision 56d68f1e19ff848c889ecfa71d3a06340ff64892)
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