]> gitweb.factorcode.org Git - factor.git/blob - basis/compiler/tests/callstack-overflow.factor
VM: fix one callstack overflow problem by "unlocking" the callstacks
[factor.git] / basis / compiler / tests / callstack-overflow.factor
1 USING: accessors classes.struct continuations kernel kernel.private literals
2 math memory sequences system threads.private tools.dispatch.private
3 tools.test ;
4 QUALIFIED: vm
5 IN: compiler.tests.callstack-overflow
6
7 ! This test file is for all callstack overflow-related problems.
8
9 : pre ( -- )
10     nano-count 0 = [ ] [ ] if ;
11
12 : post ( -- ) ;
13
14 : do-overflow ( -- )
15     pre do-overflow post ;
16
17 : recurse ( -- ? )
18     [ do-overflow f ] [ ] recover second ERROR-CALLSTACK-OVERFLOW = ;
19
20 : overflow-c ( -- ) overflow-c overflow-c ;
21
22 : overflow/w-primitive ( -- )
23     reset-dispatch-stats overflow/w-primitive post ;
24
25 : get-context ( -- ctx ) context vm:context memory>struct ;
26
27 : remaining-stack ( -- n )
28     get-context [ callstack-top>> ] [ callstack-seg>> start>> ] bi - ;
29
30 : overflow/w-compact-gc ( -- )
31     remaining-stack dup 500 < [
32         drop compact-gc
33     ] [ drop overflow/w-compact-gc ] if post ;
34
35 ! The VM cannot recover from callstack overflow on Windows, because no
36 ! facility exists to run memory protection fault handlers on an
37 ! alternate callstack. So we punt on the whole test-suite.
38 os windows? [
39
40     ! This tries to verify that enough bytes are cut off from the
41     ! callstack to run the error handler. It appears that the previous
42     ! limit of 1024 bytes didn't give the gc enough stack space to
43     ! work with, so we bumped that limit to 16384.
44     { t } [
45         10 [ recurse ] replicate [ ] all?
46     ] unit-test
47
48     ! ! See how well callstack overflow is handled
49     ! [ clear drop ] must-fail
50     !
51     ! : callstack-overflow callstack-overflow f ;
52     ! [ callstack-overflow ] must-fail
53     [ overflow-c ] [
54         2 head ${ "kernel-error" ERROR-CALLSTACK-OVERFLOW } =
55     ] must-fail-with
56
57     ! The way this is problematic is because a primitive is
58     ! involved. reset-dispatch-stats is called, decreasing RSP by cell
59     ! bytes and then there is < 0x20 bytes stack left. Then SUB RSP,
60     ! 0x18 is called to setup the call frame. Then the context is
61     ! saved and ctx->callstack_top is set to RSP - 8 which is below
62     ! the stack limit. Then dereferencing ctx->callstack_top segfaults
63     ! so we need to handle the case specially in
64     ! dispatch_non_resumable_signal().
65     [ overflow/w-primitive ] [
66         2 head ${ "kernel-error" ERROR-CALLSTACK-OVERFLOW } =
67     ] must-fail-with
68
69     ! Load up the stack until there is < 500 bytes of it left. Then
70     ! run a big gc cycle. 500 bytes isn't enough, so a callstack
71     ! overflow would occur during the gc which we can't handle. The
72     ! solution is to for the duration of the gc unlock the segment's
73     ! lower guard page which gives it pagesize (4096) more bytes to
74     ! play with.
75     { } [ overflow/w-compact-gc ] unit-test
76 ] unless