1
# use strict; mleich: disabled because of eval `cat $config_file`;
7
10
use GenTest::Constants;
8
11
use GenTest::Grammar;
12
use GenTest::Properties;
9
13
use GenTest::Simplifier::Grammar;
13
17
# RQG grammar simplification with an oracle() function based on
14
# 1. RQG exit status codes (-> @desired_status_codes)
15
# 2. expected RQG protocol output (-> @expected_output)
18
# 1. RQG exit status codes (-> desired_status_codes)
19
# 2. expected RQG protocol output (-> expected_output)
16
20
# Hint: 2. will be not checked if 1. already failed
18
# You need to adjusted parameters to your use case and environment.
19
# 1. Copy simplify-grammar_template.cfg to for exanple 1.cfg
22
# You need to adjust parameters to your use case and environment.
23
# 1. Copy simplify-grammar_template.cfg to for example 1.cfg
20
24
# 2. Adjust the settings
21
# 2. perl util/simplify-grammar.pl 1.cfg
25
# 2. perl util/simplify-grammar.pl --config 1.cfg
23
# This script is used to simplify grammar files to the smallest form that will still reproduce the desired outcome
24
# For the purpose, the GenTest::Simplifier::Grammar module provides progressively simple grammars, and we
25
# define an oracle() function that runs those grammars with the RQG and reports if the RQG returns the desired
26
# status code (usually something like STATUS_SERVER_CRASHED
27
# This script is used to simplify grammar files to the smallest form
28
# that will still reproduce the desired outcome
29
# For the purpose, the GenTest::Simplifier::Grammar module provides
30
# progressively simple grammars, and we define an oracle() function
31
# that runs those grammars with the RQG and reports if the RQG returns
32
# the desired status code (usually something like
33
# STATUS_SERVER_CRASHED
28
35
# For more information, please see:
30
37
# http://forge.mysql.com/wiki/RandomQueryGeneratorSimplification
33
# Preload user-modifiable settings
34
#---------------------------------
35
my $config_file = $ARGV[0];
36
if ( ! -f $config_file ) {
37
say ("config_file ('$config_file') is not a plain file");
42
my $initial_grammar_file;
51
my @desired_status_codes;
53
eval `cat $config_file`;
43
GetOptions($options, 'config=s', 'trials=i','storage=s','expected_output=s@');
44
my $config = GenTest::Properties->new(options => $options,
45
legal => ['desired_status_codes',
47
'initial_grammar_file',
55
defaults => {expected_output => []},
59
say("SIMPLIFY RQG GRAMMAR BASED ON EXPECTED CONTENT WITHIN SOME FILE");
60
say("---------------------------------------------------------------");
62
say("---------------------------------------------------------------");
64
## Calculate mysqld and rqg options
66
my $mysqlopt = $config->genOpt('--mysqld=--', $config->rqg_options->{mysqld});
68
## The one below is a hack.... Need support for nested options like these
69
delete $config->rqg_options->{mysqld};
71
my $rqgoptions = $config->genOpt('--', 'rqg_options');
55
73
# Determine some runtime parameter, check parameters, ....
59
77
say("The ID of this run is $run_id.");
61
open(INITIAL_GRAMMAR, $initial_grammar_file) or die "Umable to open '$initial_grammar_file': $!";;
62
read(INITIAL_GRAMMAR, my $initial_grammar , -s $initial_grammar_file);
79
open(INITIAL_GRAMMAR, $config->initial_grammar_file)
80
or croak "Unable to open '$config->initial_grammar_file': $!";;
81
read(INITIAL_GRAMMAR, my $initial_grammar , -s $config->initial_grammar_file);
63
82
close(INITIAL_GRAMMAR);
65
if ( ! -d $vardir_prefix ) {
66
say ("vardir_prefix '$vardir_prefix' is not an existing directory");
84
if ( ! -d $config->vardir_prefix ) {
85
croak("vardir_prefix '".
86
$config->vardir_prefix.
87
"' is not an existing directory");
70
90
# Calculate a unique vardir (use $MTR_BUILD_THREAD or $run_id)
71
my $vardir = $vardir_prefix.'/var_'.$run_id;
92
my $vardir = $config->vardir_prefix.'/var_'.$run_id;
73
push @mtr_options, "--vardir=$vardir";
94
push my @mtr_options, "--vardir=$vardir";
76
say ("storage ('$storage') is not an existing directory");
96
if ( ! -d $config->storage) {
97
croak("storage ('$config->storage') is not an existing directory");
80
$storage = $storage.'/'.$run_id;
99
my $storage = $config->storage.'/'.$run_id;
100
say "Storage is $storage";
85
say("SIMPLIFY RQG GRAMMAR BASED ON EXPECTED CONTENT WITHIN SOME FILE");
86
say("---------------------------------------------------------------");
87
say("rqg_options : @rqg_options ");
88
say("initial_grammar_file : $initial_grammar_file ");
89
say("desired_status_codes : @desired_status_codes ");
90
say("expected_output : @expected_output ");
91
say("trials : $trials ");
92
say("initial_seed : $initial_seed ");
93
say("storage : $storage ");
94
say("vardir_prefix : $vardir_prefix ");
95
say("run_id : $run_id");
96
say("vardir : $vardir ");
97
say("---------------------------------------------------------------");
100
my $good_seed = $initial_seed;
105
my $good_seed = $config->initial_seed;
102
107
my $simplifier = GenTest::Simplifier::Grammar->new(
103
grammar_flags => $grammar_flags,
107
my $oracle_grammar = shift;
109
foreach my $trial (1..$trials) {
110
say("run_id = $run_id; iteration = $iteration; trial = $trial");
112
# $current_seed -- The seed value to be used for the next run.
113
# The test results of many grammars are quite sensitive to the seed value.
114
# 1. Run the first trial on the initial grammar with $initial_seed .
115
# This should raise the chance that the initial oracle check passes.
116
# 2. Run the first trial on a just simplified grammar with the last successfull
117
# seed value. In case the last simplification did remove some random determined
118
# we should have a bigger likelihood to reach the expected result.
119
# 3. In case of "threads = 1" it turned out that after a minor simplification the desired
120
# bad effect disappeared sometimes on the next run with the same seed value whereas
121
# a different seed value was again successful. Therefore we manipulate the seed value.
122
# In case of "threads > 1" this manipulation might be not required, but it will not
123
# make the conditions worse.
124
my $current_seed = $good_seed - 1 + $trial;
126
# Note(mleich): The grammar used is iteration specific. Don't store per trial.
127
# Shouldn't the next command be outside of the loop ?
128
my $current_grammar = $storage.'/'.$iteration.'.yy';
129
my $current_rqg_log = $storage.'/'.$iteration.'-'.$trial.'.log';
130
my $errfile = $vardir.'/log/master.err';
131
open (GRAMMAR, ">$current_grammar") or die "unable to create $current_grammar : $!";
132
print GRAMMAR $oracle_grammar;
135
my $start_time = Time::HiRes::time();
138
# In case of "threads = 1" it turned out that after a minor simplification the desired
139
# bad effect disappeared sometimes on the next run with the same seed value whereas
140
# a different seed value was again successful. Therefore we manipulate the seed value.
141
# In case of "threads > 1" this manipulation might be not required, but it will not
142
# make the conditions worse.
143
say("perl runall.pl ".join(' ', @rqg_options)." --grammar=$current_grammar --vardir=$vardir --seed=$current_seed 2>&1 >$current_rqg_log");
144
my $rqg_status = system("perl runall.pl ".join(' ', @rqg_options)." --grammar=$current_grammar --vardir=$vardir --seed=$current_seed 2>&1 >$current_rqg_log");
146
$rqg_status = $rqg_status >> 8;
148
my $end_time = Time::HiRes::time();
149
my $duration = $end_time - $start_time;
151
say("rqg_status = $rqg_status; duration = $duration");
153
return ORACLE_ISSUE_NO_LONGER_REPEATABLE if $rqg_status == STATUS_ENVIRONMENT_FAILURE;
155
foreach my $desired_status_code (@desired_status_codes) {
156
if (($rqg_status == $desired_status_code) ||
157
(($rqg_status != 0) && ($desired_status_code == STATUS_ANY_ERROR))) {
158
# "backtrace" output (independend of server crash or RQG kills the server) is in $current_rqg_log
159
open (my $my_logfile,'<'.$current_rqg_log) or die "unable to open $current_rqg_log : $!";
160
# If open (above) did not fail than size determination must be successful.
161
my @filestats = stat($current_rqg_log);
162
my $filesize = $filestats[7];
163
my $offset = $filesize - $search_var_size;
164
# Of course read fails if $offset < 0
165
if ( $offset < 0 ) { $offset = 0 } ;
166
read($my_logfile, my $rqgtest_output, $search_var_size, $offset );
168
# Debug print("$rqgtest_output");
170
# Every element of @expected_output must be found in $rqgtest_output.
172
foreach my $expected_output (@expected_output) {
173
if ($rqgtest_output =~ m{$expected_output}sio) {
174
say ("###### Found pattern: $expected_output ######");
176
say ("###### Not found pattern: $expected_output ######");
181
if ( 1 == $success ) {
182
say ("###### SUCCESS with $current_grammar ######");
183
$good_seed = $current_seed;
184
return ORACLE_ISSUE_STILL_REPEATABLE;
186
} # End of check if the output matches given string patterns
187
} # End of loop over @desired_status_codes
188
} # End of loop over the trials
189
return ORACLE_ISSUE_NO_LONGER_REPEATABLE;
108
grammar_flags => $config->grammar_flags,
111
my $oracle_grammar = shift;
113
foreach my $trial (1..$config->trials) {
114
say("run_id = $run_id; iteration = $iteration; trial = $trial");
116
# $current_seed -- The seed value to be used for the next run.
117
# The test results of many grammars are quite sensitive to the
119
# 1. Run the first trial on the initial grammar with
120
# $config->initial_seed . This should raise the chance
121
# that the initial oracle check passes.
122
# 2. Run the first trial on a just simplified grammar with the
123
# last successfull seed value. In case the last
124
# simplification did remove some random determined we
125
# should have a bigger likelihood to reach the expected
127
# 3. In case of "threads = 1" it turned out that after a minor
128
# simplification the desired bad effect disappeared
129
# sometimes on the next run with the same seed value
130
# whereas a different seed value was again
131
# successful. Therefore we manipulate the seed value. In
132
# case of "threads > 1" this manipulation might be not
133
# required, but it will not make the conditions worse.
134
my $current_seed = $good_seed - 1 + $trial;
136
# Note(mleich): The grammar used is iteration specific. Don't
137
# store per trial. Shouldn't the next command be
138
# outside of the loop ?
139
my $current_grammar = $storage.'/'.$iteration.'.yy';
140
my $current_rqg_log = $storage.'/'.$iteration.'-'.$trial.'.log';
141
my $errfile = $vardir.'/log/master.err';
142
open (GRAMMAR, ">$current_grammar")
143
or croak "unable to create $current_grammar : $!";
144
print GRAMMAR $oracle_grammar;
147
my $start_time = Time::HiRes::time();
149
# Note(mleich): In case of "threads = 1" it turned out that
150
# after a minor simplification the desired bad effect
151
# disappeared sometimes on the next run with the same
152
# seed value whereas a different seed value was again
153
# successful. Therefore we manipulate the seed value. In
154
# case of "threads > 1" this manipulation might be not
155
# required, but it will not make the conditions worse.
158
"perl runall.pl $rqgoptions $mysqlopt ".
159
"--grammar=$current_grammar ".
161
"--seed=$current_seed 2>&1 >$current_rqg_log";
164
my $rqg_status = system($rqgcmd);
165
$rqg_status = $rqg_status >> 8;
167
my $end_time = Time::HiRes::time();
168
my $duration = $end_time - $start_time;
170
say("rqg_status = $rqg_status; duration = $duration");
172
return ORACLE_ISSUE_NO_LONGER_REPEATABLE
173
if $rqg_status == STATUS_ENVIRONMENT_FAILURE;
175
foreach my $desired_status_code (@{$config->desired_status_codes}) {
176
if (($rqg_status == $desired_status_code) ||
177
(($rqg_status != 0) && ($desired_status_code == STATUS_ANY_ERROR))) {
178
# "backtrace" output (independend of server crash
179
# or RQG kills the server) is in $current_rqg_log
180
open (my $my_logfile,'<'.$current_rqg_log)
181
or croak "unable to open $current_rqg_log : $!";
182
# If open (above) did not fail than size
183
# determination must be successful.
184
my @filestats = stat($current_rqg_log);
185
my $filesize = $filestats[7];
186
my $offset = $filesize - $config->search_var_size;
187
# Of course read fails if $offset < 0
188
$offset = 0 if $offset < 0;
189
read($my_logfile, my $rqgtest_output, $config->search_var_size, $offset );
191
# Debug print("$rqgtest_output");
193
# Every element of @expected_output must be found
194
# in $rqgtest_output.
196
foreach my $expected_output (@{$config->expected_output}) {
197
if ($rqgtest_output =~ m{$expected_output}sio) {
198
say ("###### Found pattern: $expected_output ######");
200
say ("###### Not found pattern: $expected_output ######");
206
say ("###### SUCCESS with $current_grammar ######");
207
$good_seed = $current_seed;
208
return ORACLE_ISSUE_STILL_REPEATABLE;
210
} # End of check if the output matches given string patterns
211
} # End of loop over desired_status_codes
212
} # End of loop over the trials
213
return ORACLE_ISSUE_NO_LONGER_REPEATABLE;
193
217
my $simplified_grammar = $simplifier->simplify($initial_grammar);