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