5 ######################################################
6 # Binary search script for switchback
7 # Finds bad basic block for seg faults and bad output.
9 # To test output, you need to create test_ref
10 # test_ref should hold the correct output for running the test_xxx program:
11 # - Everything between (not including) /^---START---$/ and /^---STOP---$/
12 # - But NOT including output from /^---begin SWITCHBACK/
13 # to /^--- end SWITCHBACK/ inclusive
15 # This script can't handle other vex output,
16 # so e.g switchback.c::DEBUG_TRACE_FLAGS should be 0
19 ######################################################
21 use constant DEBUG
=> 0;
22 use constant CONST_N_MAX
=> 10000000000;
23 use constant CONST_N_MUL
=> 2;
25 my $SWITCHBACK = "./switchback";
29 my $GIVEN_LAST_GOOD = -1;
30 my $GIVEN_LAST_BAD = -1;
35 ######################################################
43 print "Usage: binary_switchback.pl test_ref [last_good [last_bad]]\n";
45 print " test_ref = reference output from test_xxx\n";
46 print " last_good = last known good bb (search space minimum)\n";
47 print " last_bad = last known bad bb (search space maximum)\n";
58 ######################################################
59 # Get & check cmdline args
60 # - if given, override global vars.
62 if (@ARGV < 1 || @ARGV > 3) {
63 QuitUsage
"Error: Bad num args\n";
68 if ( ! -x
"$SWITCHBACK" ) {
69 QuitUsage
"File doesn't exist | not executable: '$SWITCHBACK'\n";
73 $N_LAST_GOOD = $ARGV[1];
74 $GIVEN_LAST_GOOD = $N_LAST_GOOD;
75 if (! ($N_LAST_GOOD =~ /^\d*$/)) {
76 QuitUsage
"Error: bad arg for #last_good\n";
78 if ($N_LAST_GOOD >= CONST_N_MAX
) {
79 QuitUsage
"Error: #last_good >= N_MAX(".CONST_N_MAX
.")\n";
83 $N_LAST_BAD = $ARGV[2];
84 $GIVEN_LAST_BAD = $N_LAST_BAD;
85 if (! ($N_LAST_BAD =~ /^\d*$/)) {
86 QuitUsage
"Error: bad arg for 'last_bad'\n";
91 if ($N_LAST_BAD != -1) {
93 my $diff = $N_LAST_BAD - $N_LAST_GOOD;
94 $N_START = $N_LAST_GOOD + ($diff - ($diff % 2)) / 2;
96 # No known end: Start at beginning:
97 if ($N_LAST_GOOD > 0) { # User-given last_good
98 $N_START = $N_LAST_GOOD;
100 $N_START = 100; # Some reasonable number.
104 ######################################################
105 # Sanity checks (shouldn't ever happen)
107 if ($N_START < $N_LAST_GOOD) {
108 print "Program Error: start < last_good\n";
111 if ($N_LAST_BAD != -1 && $N_START >= $N_LAST_BAD) {
112 print "Program Error: start >= last_bad\n";
115 if ($N_START < 1 || $N_START > CONST_N_MAX
) {
116 print "Program Error: Bad N_START: '$N_START'\n";
119 if ($N_LAST_GOOD < 0 || $N_LAST_GOOD > CONST_N_MAX
) {
120 print "Program Error: Bad N_LAST_GOOD: '$N_LAST_GOOD'\n";
123 if ($N_LAST_BAD < -1 || $N_LAST_BAD > CONST_N_MAX
) {
124 print "Program Error: Bad N_LAST_BAD: '$N_LAST_BAD'\n";
133 ######################################################
136 # Run switchback for test, for N bbs
137 # returns output results
140 if ($n < 0 || $n > CONST_N_MAX
) {
141 print "Error SwitchBack: Bad N: '$n'\n";
144 my $TMPFILE = ".switchback_output.$n";
146 print "=== Calling switchback for bb $n ===\n";
148 system("$SWITCHBACK $n >& $TMPFILE");
152 print "Error running switchback - Quitting...\n---\n";
153 open(INFILE
, "$TMPFILE");
157 unlink($TMPFILE) if (! DEBUG
);
162 print "Ctrl-C pressed - Quitting...\n";
163 unlink($TMPFILE) if (! DEBUG
);
169 print "failed to execute: $!\n";
172 printf "child died with signal %d, %s coredump\n",
173 ($ret & 127), ($ret & 128) ?
'with' : 'without';
176 printf "child exited with value %d\n", $ret >> 8;
179 if ($ret != 0) { # Err: maybe seg fault
180 open(INFILE
, "$TMPFILE");
181 my @results = <INFILE
>;
184 while (@results && !((shift @results) =~ /^---START---/)) {}
187 unlink($TMPFILE) if (! DEBUG
);
191 open(INFILE
, "$TMPFILE");
192 my @results = <INFILE
>;
195 unlink($TMPFILE) if (! DEBUG
);
199 # Returns N simulated bbs from output lines
200 sub get_N_simulated
{
201 my @lines = @
{$_[0]};
202 pop @lines; # not the first...
203 my $line = pop @lines; # ...but the second line.
207 if (($n) = ($line =~ /^(\d*) bbs simulated$/)) {
210 print "Error: Didn't find N bbs simultated, from output lines\n";
214 # Calls test script to compare current output lines with a reference.
215 # Returns 1 on success, 0 on failure
217 my @lines = @
{$_[0]};
219 my $ref_output = "$TEST_REF";
221 # Get the current section we want to compare:
225 foreach my $line(@lines) {
227 if ($line =~ /^---STOP---$/) { last; } # we're done
229 # output might be messed up here...
230 if ($line =~ /^.*---begin SWITCHBACK/) {
231 ($halfline) = ($line =~ /^(.*)---begin SWITCHBACK/);
232 $ok = 0; # stop on prev line
237 if ($halfline ne "") { # Fix broken line
238 $line = $halfline.$line;
243 if ($line =~ /^vex /) { next; }
245 push(@newlines, $line);
248 if ($line =~ /^---START---$/) { # start on next line
252 if ($line =~ /^--- end SWITCHBACK/) { # start on next line
259 open(OUTFILE
, ">.filtered_output.$n");
260 print OUTFILE
join("\n",@newlines);
264 # Read in reference lines
265 open(REFERENCE
, "$ref_output") || die "Error: Couldn't open $ref_output\n";
266 my @ref_lines = <REFERENCE
>;
269 # Compare reference lines with current:
272 foreach my $ref_line(@ref_lines) {
274 my $line = $newlines[$i++];
276 if ($ref_line ne $line) {
277 print "\nMismatch on output:\n";
278 print "ref: '$ref_line'\n";
279 print "new: '$line'\n\n";
292 ######################################################
296 print "\n------------\n";
297 print "START: N=$N_START\n";
298 print "START: lg=$N_LAST_GOOD\n";
299 print "START: lb=$N_LAST_BAD\n";
300 print "START: GIVEN_LAST_GOOD=$GIVEN_LAST_GOOD\n";
301 print "START: GIVEN_LAST_BAD =$GIVEN_LAST_BAD\n";
310 print "\n------------\n";
311 print "SOL: lg=$N_LAST_GOOD\n";
312 print "SOL: lb=$N_LAST_BAD\n";
316 print "Error: $N<0\n";
322 @sb_output = SwitchBack
($N);
324 if (@sb_output == 0) { # Switchback failed - maybe seg fault
329 open(fileOUT
, ">.retrieved_output.$N") or die("Can't open file for writing: $!");
330 print fileOUT
@sb_output;
334 # If we're ok so far (no seg faults) then test for correct output
336 $ok = TestOutput
( \
@sb_output, $N );
340 if (get_N_simulated
(\
@sb_output) < $N) { # Done: No bad bbs
344 if ($N_LAST_BAD == -1) {
345 # No upper bound for search space
346 # Try again with a bigger N
350 if ($N > CONST_N_MAX
) {
351 print "\nError: Maxed out N($N): N_MAX=".CONST_N_MAX
."\n";
352 print "\nWe're either in a loop, or this is a big test program (increase N_MAX)\n\n";
356 print "Looks good so far: Trying bigger N...\n\n";
362 # Narrow the search space:
363 if ($ok) { $N_LAST_GOOD = $N; }
364 else { $N_LAST_BAD = $N; }
366 # Calculate next step:
367 my $diff = $N_LAST_BAD - $N_LAST_GOOD;
368 $diff = $diff - ($diff % 2);
369 my $step = $diff / 2;
372 print "Error: step = $step\n";
376 # This our last run-through?
378 $N = $N_LAST_GOOD + $step; # Keep on going...
380 last; # Get outta here
384 print "\nEOL: ok=$ok\n";
385 print "EOL: lg=$N_LAST_GOOD\n";
386 print "EOL: lb=$N_LAST_BAD\n";
387 print "EOL: s=$step\n";
394 ######################################################
395 # Done: Report results
397 print "\n============================================\n";
398 print "Done searching.\n\n";
400 if ($N_LAST_BAD != -1 && $N != $N_LAST_BAD) {
401 print "Getting output for last bad bb:\n";
402 @sb_output = SwitchBack
($N_LAST_BAD);
408 print "*** Success! No bad bbs found. ***\n";
410 if ($N_LAST_BAD == $GIVEN_LAST_BAD) {
411 print "*** No failures detected within given bb range ***\n";
412 print " - check given 'last_bad' argument\n";
414 if ($N_LAST_BAD == $GIVEN_LAST_GOOD) {
415 print "*** Failed on bb given as last_good ***\n";
416 print " - decrease the 'last_good' argument\n";
418 print "*** Failure: Last failed switchback bb: $N_LAST_BAD ***\n";
419 print "Hence bad bb: ". ($N_LAST_BAD - 1) ."\n";
426 print "END: lg=$N_LAST_GOOD\n";
427 print "END: lb=$N_LAST_BAD\n";
428 print "END: GIVEN_LAST_BAD=$GIVEN_LAST_BAD\n";