# Torturing Bison. -*- Autotest -*- # Copyright (C) 2001-2002, 2004-2007, 2009-2012 Free Software # Foundation, Inc. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . AT_BANNER([[Torture Tests.]]) # AT_INCREASE_DATA_SIZE(SIZE) # --------------------------- # Try to increase the data size to SIZE KiB if possible. m4_define([AT_INCREASE_DATA_SIZE], [data_limit=`(ulimit -S -d) 2>/dev/null` case $data_limit in [[0-9]]*) if test "$data_limit" -lt $1; then AT_CHECK([ulimit -S -d $1 || exit 77]) ulimit -S -d $1 fi esac]) ## ------------------------------------- ## ## Creating a large artificial grammar. ## ## ------------------------------------- ## # AT_DATA_TRIANGULAR_GRAMMAR(FILE-NAME, SIZE) # ------------------------------------------- # Create FILE-NAME, containing a self checking parser for a huge # triangular grammar. m4_define([AT_DATA_TRIANGULAR_GRAMMAR], [AT_BISON_OPTION_PUSHDEFS AT_DATA([[gengram.pl]], [[#! /usr/bin/perl -w use strict; my $max = $ARGV[0] || 10; print < #include #include #define MAX $max ]AT_YYLEX_DECLARE[ ]AT_YYERROR_DECLARE[ %} %union { int val; }; %token END "end" %type exp input EOF for my $size (1 .. $max) { print "%token t$size $size \"$size\"\n"; }; print < MAX) return 0; else if (inner > outer) { inner = 1; ++outer; return END; } return inner++; } int main (void) { yydebug = !!getenv ("YYDEBUG"); return yyparse (); } EOF ]]) AT_BISON_OPTION_POPDEFS AT_CHECK([$PERL -w ./gengram.pl $2 || exit 77], 0, [stdout]) mv stdout $1 ]) ## -------------- ## ## Big triangle. ## ## -------------- ## AT_SETUP([Big triangle]) # I have been able to go up to 2000 on my machine. # I tried 3000, a 29Mb grammar file, but then my system killed bison. # With 500 and the new parser, which consume far too much memory, # it gets killed too. Of course the parser is to be cleaned. AT_DATA_TRIANGULAR_GRAMMAR([input.y], [200]) AT_BISON_CHECK_NO_XML([-v -o input.c input.y]) AT_COMPILE([input]) AT_PARSER_CHECK([./input]) AT_CLEANUP # AT_DATA_HORIZONTAL_GRAMMAR(FILE-NAME, SIZE) # ------------------------------------------- # Create FILE-NAME, containing a self checking parser for a huge # horizontal grammar. m4_define([AT_DATA_HORIZONTAL_GRAMMAR], [AT_BISON_OPTION_PUSHDEFS AT_DATA([[gengram.pl]], [[#! /usr/bin/perl -w use strict; my $max = $ARGV[0] || 10; print < #include #define MAX $max ]AT_YYLEX_DECLARE[ ]AT_YYERROR_DECLARE[ %} %token EOF for my $size (1 .. $max) { print " t$size $size \"$size\"\n"; }; print < ]AT_YYERROR_DEFINE[ static int yylex (void) { static int counter = 1; if (counter <= MAX) return counter++; assert (counter++ == MAX + 1); return 0; } int main (void) { yydebug = !!getenv ("YYDEBUG"); return yyparse (); } EOF ]]) AT_CHECK([$PERL -w ./gengram.pl $2 || exit 77], 0, [stdout]) mv stdout $1 AT_BISON_OPTION_POPDEFS ]) ## ---------------- ## ## Big horizontal. ## ## ---------------- ## AT_SETUP([Big horizontal]) # I have been able to go up to 10000 on my machine, but I had to # increase the maximum stack size (* 100). It gave: # # input.y 263k # input.tab.c 1.3M # input 453k # # gengram.pl 10000 0.70s user 0.01s sys 99% cpu 0.711 total # bison input.y 730.56s user 0.53s sys 99% cpu 12:12.34 total # gcc -Wall input.tab.c -o input 5.81s user 0.20s sys 100% cpu 6.01 total # ./input 0.00s user 0.01s sys 108% cpu 0.01 total # AT_DATA_HORIZONTAL_GRAMMAR([input.y], [1000]) # GNU m4 requires about 70 MiB for this test on a 32-bit host. # Ask for 200 MiB, which should be plenty even on a 64-bit host. AT_INCREASE_DATA_SIZE(204000) AT_BISON_CHECK_NO_XML([-v -o input.c input.y]) AT_COMPILE([input]) AT_PARSER_CHECK([./input]) AT_CLEANUP # AT_DATA_LOOKAHEAD_TOKENS_GRAMMAR(FILE-NAME, SIZE) # -------------------------------------------------- # Create FILE-NAME, containing a self checking parser for a grammar # requiring SIZE lookahead tokens. m4_define([AT_DATA_LOOKAHEAD_TOKENS_GRAMMAR], [AT_BISON_OPTION_PUSHDEFS AT_DATA([[gengram.pl]], [[#! /usr/bin/perl -w use strict; use Text::Wrap; my $max = $ARGV[0] || 10; print < # include # include # define MAX $max ]AT_YYLEX_DECLARE[ ]AT_YYERROR_DECLARE[ %} %union { int val; }; %type input exp %token token EOF print wrap ("%type ", " ", map { "n$_" } (1 .. $max)), "\n"; print "%token\n"; for my $count (1 .. $max) { print " t$count $count \"$count\"\n"; }; print < MAX) { assert (counter++ == MAX + 1); return 0; } if (return_token) { return_token = 0; return token; } return_token = 1; return counter++; } int main (void) { yydebug = !!getenv ("YYDEBUG"); return yyparse (); } EOF ]]) AT_CHECK([$PERL -w ./gengram.pl $2 || exit 77], 0, [stdout]) mv stdout $1 AT_BISON_OPTION_POPDEFS ]) ## ------------------------ ## ## Many lookahead tokens. ## ## ------------------------ ## AT_SETUP([Many lookahead tokens]) AT_DATA_LOOKAHEAD_TOKENS_GRAMMAR([input.y], [1000]) # GNU m4 requires about 70 MiB for this test on a 32-bit host. # Ask for 200 MiB, which should be plenty even on a 64-bit host. AT_INCREASE_DATA_SIZE(204000) AT_BISON_CHECK([-v -o input.c input.y]) AT_COMPILE([input]) AT_PARSER_CHECK([./input]) AT_CLEANUP # AT_DATA_STACK_TORTURE(C-PROLOGUE, [BISON-DECLS]) # ------------------------------------------------ # A parser specialized in torturing the stack size. m4_define([AT_DATA_STACK_TORTURE], [AT_BISON_OPTION_PUSHDEFS([$2]) # A grammar of parens growing the stack thanks to right recursion. # exp: AT_DATA_GRAMMAR([input.y], [[%{ #include #include #include #include ]$1[ ]AT_YYLEX_DECLARE[ ]AT_YYERROR_DECLARE[ %} ]$2[ %error-verbose %debug %token WAIT_FOR_EOF %% exp: WAIT_FOR_EOF exp | ; %% ]AT_YYERROR_DEFINE[ #include static int yylex (void) { assert (0 <= yylval); if (yylval--) return WAIT_FOR_EOF; else return EOF; } /* Return argv[1] as an int. */ static int get_args (int argc, const char **argv) { int res; char *endp; assert (argc == 2); res = strtol (argv[1], &endp, 10); assert (argv[1] != endp); assert (0 <= res); assert (res <= INT_MAX); assert (errno != ERANGE); return res; } int main (int argc, const char **argv) { YYSTYPE yylval_init = get_args (argc, argv); int status = 0; int count; ]m4_bmatch([$2], [api.push-pull both], [[ yypstate *ps = yypstate_new (); ]])[ yydebug = 1; for (count = 0; count < 2; ++count) { int new_status; yylval = yylval_init; new_status = ]m4_bmatch([$2], [api.push-pull both], [[yypull_parse (ps)]], [[yyparse ()]])[; if (count == 0) status = new_status; else assert (new_status == status); }]m4_bmatch([$2], [api.push-pull both],[[ yypstate_delete (ps);]])[ return status; } ]]) AT_BISON_OPTION_POPDEFS([$2]) AT_BISON_CHECK([-o input.c input.y]) AT_COMPILE([input]) ]) ## -------------------------------------- ## ## Exploding the Stack Size with Alloca. ## ## -------------------------------------- ## AT_SETUP([Exploding the Stack Size with Alloca]) m4_pushdef([AT_USE_ALLOCA], [[ #if (defined __GNUC__ || defined __BUILTIN_VA_ARG_INCR \ || defined _AIX || defined _MSC_VER || defined _ALLOCA_H) # define YYSTACK_USE_ALLOCA 1 #endif ]]) AT_DATA_STACK_TORTURE([AT_USE_ALLOCA]) # Below the limit of 200. AT_PARSER_CHECK([./input 20], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # Two enlargements: 2 * 2 * 200. AT_PARSER_CHECK([./input 900], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # Fails: beyond the limit of 10,000 (which we don't reach anyway since we # multiply by two starting at 200 => 5120 is the last possible). AT_PARSER_CHECK([./input 10000], 2, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # The push parser can't use alloca since the stacks can't be locals. This test # just helps guarantee we don't let the YYSTACK_USE_ALLOCA feature affect # push parsers. AT_DATA_STACK_TORTURE([AT_USE_ALLOCA], [[%define api.push-pull both ]]) AT_PARSER_CHECK([./input 20], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_PARSER_CHECK([./input 900], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_PARSER_CHECK([./input 10000], 2, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) m4_popdef([AT_USE_ALLOCA]) AT_CLEANUP ## -------------------------------------- ## ## Exploding the Stack Size with Malloc. ## ## -------------------------------------- ## AT_SETUP([Exploding the Stack Size with Malloc]) m4_pushdef([AT_USE_ALLOCA], [[#define YYSTACK_USE_ALLOCA 0]]) AT_DATA_STACK_TORTURE([AT_USE_ALLOCA]) # Below the limit of 200. AT_PARSER_CHECK([./input 20], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # Two enlargements: 2 * 2 * 200. AT_PARSER_CHECK([./input 900], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) # Fails: beyond the limit of 10,000 (which we don't reach anyway since we # multiply by two starting at 200 => 5120 is the possible). AT_PARSER_CHECK([./input 10000], 2, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_DATA_STACK_TORTURE([AT_USE_ALLOCA], [[%define api.push-pull both ]]) AT_PARSER_CHECK([./input 20], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_PARSER_CHECK([./input 900], 0, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) AT_PARSER_CHECK([./input 10000], 2, [], [ignore], [[VALGRIND_OPTS="$VALGRIND_OPTS --log-fd=1"]]) m4_popdef([AT_USE_ALLOCA]) AT_CLEANUP