blob: 4b096ee92824b98ae9311e357c45ab3e0046c57b [file] [log] [blame]
cerionab2e1912005-02-28 18:48:27 +00001#!/usr/bin/env perl
2use strict;
3use warnings;
4
5######################################################
6# Binary search script for switchback
7# Finds bad basic block for seg faults and bad output.
8#
cerion6ded3892005-12-13 21:30:48 +00009# To test output, you need to create test_ref
10# test_ref should hold the correct output for running the test_xxx program:
cerionab2e1912005-02-28 18:48:27 +000011# - Everything between (not including) /^---START---$/ and /^---STOP---$/
12# - But NOT including output from /^---begin SWITCHBACK/
13# to /^--- end SWITCHBACK/ inclusive
14#
cerion6ded3892005-12-13 21:30:48 +000015# This script can't handle other vex output,
16# so e.g switchback.c::DEBUG_TRACE_FLAGS should be 0
17#
cerionab2e1912005-02-28 18:48:27 +000018
19######################################################
20# Global consts, vars
21use constant DEBUG => 0;
cerion6ded3892005-12-13 21:30:48 +000022use constant CONST_N_MAX => 10000000000;
cerionab2e1912005-02-28 18:48:27 +000023use constant CONST_N_MUL => 2;
24
25my $SWITCHBACK = "./switchback";
26my $N_START = 0;
27my $N_LAST_GOOD = 0;
28my $N_LAST_BAD = -1;
cerion87ae7832005-03-01 20:31:53 +000029my $GIVEN_LAST_GOOD = -1;
cerionab2e1912005-02-28 18:48:27 +000030my $GIVEN_LAST_BAD = -1;
cerion6ded3892005-12-13 21:30:48 +000031my $TEST_REF;
cerionab2e1912005-02-28 18:48:27 +000032
33
34
35######################################################
36# Helper functions
37
38sub Exit {
39 exit $_[0];
40}
41
42sub Usage {
cerion6ded3892005-12-13 21:30:48 +000043 print "Usage: binary_switchback.pl test_ref [last_good [last_bad]]\n";
cerionab2e1912005-02-28 18:48:27 +000044 print "where:\n";
cerion6ded3892005-12-13 21:30:48 +000045 print " test_ref = reference output from test_xxx\n";
cerionab2e1912005-02-28 18:48:27 +000046 print " last_good = last known good bb (search space minimum)\n";
47 print " last_bad = last known bad bb (search space maximum)\n";
48 print "\n";
49}
50
51sub QuitUsage {
52 print $_[0]."\n";
53 Usage();
54 Exit 1;
55}
56
57
58######################################################
59# Get & check cmdline args
60# - if given, override global vars.
61
62if (@ARGV < 1 || @ARGV > 3) {
63 QuitUsage "Error: Bad num args\n";
64}
65
cerion6ded3892005-12-13 21:30:48 +000066$TEST_REF = $ARGV[0];
cerionab2e1912005-02-28 18:48:27 +000067
68if ( ! -x "$SWITCHBACK" ) {
69 QuitUsage "File doesn't exist | not executable: '$SWITCHBACK'\n";
70}
71
72if (@ARGV >1) {
73 $N_LAST_GOOD = $ARGV[1];
cerion87ae7832005-03-01 20:31:53 +000074 $GIVEN_LAST_GOOD = $N_LAST_GOOD;
cerionab2e1912005-02-28 18:48:27 +000075 if (! ($N_LAST_GOOD =~ /^\d*$/)) {
76 QuitUsage "Error: bad arg for #last_good\n";
77 }
78 if ($N_LAST_GOOD >= CONST_N_MAX) {
79 QuitUsage "Error: #last_good >= N_MAX(".CONST_N_MAX.")\n";
80 }
81}
82if (@ARGV >2) {
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";
87 }
88}
89
90# Setup N_START
91if ($N_LAST_BAD != -1) {
92 # Start halfway:
93 my $diff = $N_LAST_BAD - $N_LAST_GOOD;
94 $N_START = $N_LAST_GOOD + ($diff - ($diff % 2)) / 2;
95} else {
96 # No known end: Start at beginning:
97 if ($N_LAST_GOOD > 0) { # User-given last_good
98 $N_START = $N_LAST_GOOD;
99 } else {
100 $N_START = 100; # Some reasonable number.
101 }
102}
103
104######################################################
105# Sanity checks (shouldn't ever happen)
106
107if ($N_START < $N_LAST_GOOD) {
108 print "Program Error: start < last_good\n";
109 exit 1;
110}
111if ($N_LAST_BAD != -1 && $N_START >= $N_LAST_BAD) {
112 print "Program Error: start >= last_bad\n";
113 exit 1;
114}
115if ($N_START < 1 || $N_START > CONST_N_MAX) {
116 print "Program Error: Bad N_START: '$N_START'\n";
117 exit 1;
118}
119if ($N_LAST_GOOD < 0 || $N_LAST_GOOD > CONST_N_MAX) {
120 print "Program Error: Bad N_LAST_GOOD: '$N_LAST_GOOD'\n";
121 exit 1;
122}
123if ($N_LAST_BAD < -1 || $N_LAST_BAD > CONST_N_MAX) {
124 print "Program Error: Bad N_LAST_BAD: '$N_LAST_BAD'\n";
125 exit 1;
126}
127
128
129
130
131
132
133######################################################
134# Helper functions
135
136# Run switchback for test, for N bbs
137# returns output results
138sub SwitchBack {
139 my $n = $_[0];
140 if ($n < 0 || $n > CONST_N_MAX) {
141 print "Error SwitchBack: Bad N: '$n'\n";
142 Exit 1;
143 }
144 my $TMPFILE = ".switchback_output.$n";
145
cerionfa09ac52005-03-04 14:20:14 +0000146 print "=== Calling switchback for bb $n ===\n";
cerionab2e1912005-02-28 18:48:27 +0000147
cerion6ded3892005-12-13 21:30:48 +0000148 system("$SWITCHBACK $n >& $TMPFILE");
cerionab2e1912005-02-28 18:48:27 +0000149 my $ret = $?;
150
cerionfa09ac52005-03-04 14:20:14 +0000151 if ($ret == 256) {
152 print "Error running switchback - Quitting...\n---\n";
153 open(INFILE, "$TMPFILE");
154 print <INFILE>;
155 close(INFILE);
156
157 unlink($TMPFILE) if (! DEBUG);
158 exit 0;
159 }
160
cerionab2e1912005-02-28 18:48:27 +0000161 if ($ret & 127) {
162 print "Ctrl-C pressed - Quitting...\n";
cerionfa09ac52005-03-04 14:20:14 +0000163 unlink($TMPFILE) if (! DEBUG);
cerionab2e1912005-02-28 18:48:27 +0000164 exit 0;
165 }
166
167 if (DEBUG) {
168 if ($ret == -1) {
169 print "failed to execute: $!\n";
170 }
171 elsif ($ret & 127) {
172 printf "child died with signal %d, %s coredump\n",
173 ($ret & 127), ($ret & 128) ? 'with' : 'without';
174 }
175 else {
176 printf "child exited with value %d\n", $ret >> 8;
177 }
178 }
179 if ($ret != 0) { # Err: maybe seg fault
cerion997661a2005-03-03 04:49:09 +0000180 open(INFILE, "$TMPFILE");
181 my @results = <INFILE>;
182 close(INFILE);
183
cerionfa09ac52005-03-04 14:20:14 +0000184 while (@results && !((shift @results) =~ /^---START---/)) {}
cerion997661a2005-03-03 04:49:09 +0000185 print @results;
186
cerionfa09ac52005-03-04 14:20:14 +0000187 unlink($TMPFILE) if (! DEBUG);
cerionab2e1912005-02-28 18:48:27 +0000188 return;
189 }
190
191 open(INFILE, "$TMPFILE");
192 my @results = <INFILE>;
193 close(INFILE);
194
cerionfa09ac52005-03-04 14:20:14 +0000195 unlink($TMPFILE) if (! DEBUG);
cerionab2e1912005-02-28 18:48:27 +0000196 return @results;
197}
198
199# Returns N simulated bbs from output lines
200sub get_N_simulated {
201 my @lines = @{$_[0]};
202 pop @lines; # not the first...
203 my $line = pop @lines; # ...but the second line.
204
205 chomp $line;
206 my $n;
207 if (($n) = ($line =~ /^(\d*) bbs simulated$/)) {
208 return $n;
209 }
210 print "Error: Didn't find N bbs simultated, from output lines\n";
211 Exit 1;
212}
213
214# Calls test script to compare current output lines with a reference.
215# Returns 1 on success, 0 on failure
216sub TestOutput {
217 my @lines = @{$_[0]};
218 my $n = $_[1];
cerion6ded3892005-12-13 21:30:48 +0000219 my $ref_output = "$TEST_REF";
cerionab2e1912005-02-28 18:48:27 +0000220
221 # Get the current section we want to compare:
222 my @newlines;
223 my $ok=0;
224 my $halfline = "";
225 foreach my $line(@lines) {
226 chomp $line;
227 if ($line =~ /^---STOP---$/) { last; } # we're done
228
229 # output might be messed up here...
230 if ($line =~ /^.*---begin SWITCHBACK/) {
231 ($halfline) = ($line =~ /^(.*)---begin SWITCHBACK/);
232 $ok = 0; # stop on prev line
233 }
234
235 # A valid line:
236 if ($ok) {
237 if ($halfline ne "") { # Fix broken line
238 $line = $halfline.$line;
239 $halfline = "";
240 }
cerion87ae7832005-03-01 20:31:53 +0000241
242 # Ignore Vex output
243 if ($line =~ /^vex /) { next; }
244
cerionab2e1912005-02-28 18:48:27 +0000245 push(@newlines, $line);
246 }
247
248 if ($line =~ /^---START---$/) { # start on next line
249 $ok = 1;
250 }
251
252 if ($line =~ /^--- end SWITCHBACK/) { # start on next line
253 $ok = 1;
254
255 }
256 }
257
258 if (DEBUG) {
259 open(OUTFILE, ">.filtered_output.$n");
260 print OUTFILE join("\n",@newlines);
261 close(OUTFILE);
262 }
263
264 # Read in reference lines
265 open(REFERENCE, "$ref_output") || die "Error: Couldn't open $ref_output\n";
266 my @ref_lines = <REFERENCE>;
267 close(REFERENCE);
268
269 # Compare reference lines with current:
270 my $match = 1;
271 my $i = 0;
272 foreach my $ref_line(@ref_lines) {
273 chomp $ref_line;
274 my $line = $newlines[$i++];
275 chomp $line;
276 if ($ref_line ne $line) {
277 print "\nMismatch on output:\n";
278 print "ref: '$ref_line'\n";
279 print "new: '$line'\n\n";
280 $match = 0;
281 last;
282 }
283 }
284 return $match;
285}
286
287
288
289
290
291
292######################################################
293# Do the search
294
295if (DEBUG) {
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";
cerion87ae7832005-03-01 20:31:53 +0000300 print "START: GIVEN_LAST_GOOD=$GIVEN_LAST_GOOD\n";
301 print "START: GIVEN_LAST_BAD =$GIVEN_LAST_BAD\n";
cerionab2e1912005-02-28 18:48:27 +0000302 print "\n";
303}
304
305my $N = $N_START;
306my $success = 0;
307my @sb_output;
308while (1) {
309 if (DEBUG) {
310 print "\n------------\n";
311 print "SOL: lg=$N_LAST_GOOD\n";
312 print "SOL: lb=$N_LAST_BAD\n";
313 print "SOL: N=$N\n";
314 }
315 if ($N < 0) {
316 print "Error: $N<0\n";
317 Exit 1;
318 }
319
320 my $ok = 1;
321 # Run switchback:
322 @sb_output = SwitchBack($N);
323
324 if (@sb_output == 0) { # Switchback failed - maybe seg fault
325 $ok = 0;
326 }
327
328 if (DEBUG) {
329 open(fileOUT, ">.retrieved_output.$N") or die("Can't open file for writing: $!");
330 print fileOUT @sb_output;
331 close(fileOUT);
332 }
333
334 # If we're ok so far (no seg faults) then test for correct output
335 if ($ok) {
336 $ok = TestOutput( \@sb_output, $N );
337 }
338
339 if ($ok) {
340 if (get_N_simulated(\@sb_output) < $N) { # Done: No bad bbs
341 $success = 1;
342 last;
343 }
344 if ($N_LAST_BAD == -1) {
345 # No upper bound for search space
346 # Try again with a bigger N
347
348 $N_LAST_GOOD = $N;
349 $N *= CONST_N_MUL;
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";
353 Exit 1;
354 }
cerion87ae7832005-03-01 20:31:53 +0000355 if (DEBUG) {
356 print "Looks good so far: Trying bigger N...\n\n";
357 }
cerionab2e1912005-02-28 18:48:27 +0000358 next;
359 }
360 }
361
362 # Narrow the search space:
363 if ($ok) { $N_LAST_GOOD = $N; }
364 else { $N_LAST_BAD = $N; }
365
366 # Calculate next step:
367 my $diff = $N_LAST_BAD - $N_LAST_GOOD;
368 $diff = $diff - ($diff % 2);
369 my $step = $diff / 2;
370
371 if ($step < 0) {
372 print "Error: step = $step\n";
373 Exit 1;
374 }
375
376 # This our last run-through?
377 if ($step!=0) {
378 $N = $N_LAST_GOOD + $step; # Keep on going...
379 } else {
380 last; # Get outta here
381 }
382
383 if (DEBUG) {
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";
388 print "EOL: N=$N\n";
389 }
390}
391
392
393
394######################################################
395# Done: Report results
396
397print "\n============================================\n";
398print "Done searching.\n\n";
399
400if ($N_LAST_BAD != -1 && $N != $N_LAST_BAD) {
cerion87ae7832005-03-01 20:31:53 +0000401 print "Getting output for last bad bb:\n";
cerionab2e1912005-02-28 18:48:27 +0000402 @sb_output = SwitchBack($N_LAST_BAD);
403}
404
405print @sb_output;
406print "\n\n";
407if ($success) {
408 print "*** Success! No bad bbs found. ***\n";
409} else {
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";
413 } else {
cerion87ae7832005-03-01 20:31:53 +0000414 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";
417 } else {
418 print "*** Failure: Last failed switchback bb: $N_LAST_BAD ***\n";
419 print "Hence bad bb: ". ($N_LAST_BAD - 1) ."\n";
420 }
cerionab2e1912005-02-28 18:48:27 +0000421 }
422}
423print "\n";
424if (DEBUG) {
425 print "END: N=$N\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";
429 print "\n";
430}
431Exit 0;