]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
perl script to run a binary search with switchback
authorCerion Armour-Brown <cerion@valgrind.org>
Mon, 28 Feb 2005 18:48:27 +0000 (18:48 +0000)
committerCerion Armour-Brown <cerion@valgrind.org>
Mon, 28 Feb 2005 18:48:27 +0000 (18:48 +0000)
git-svn-id: svn://svn.valgrind.org/vex/trunk@988

VEX/switchback/binary_switchback.pl [new file with mode: 0755]

diff --git a/VEX/switchback/binary_switchback.pl b/VEX/switchback/binary_switchback.pl
new file mode 100755 (executable)
index 0000000..6bcb9da
--- /dev/null
@@ -0,0 +1,402 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+######################################################
+# Binary search script for switchback
+# Finds bad basic block for seg faults and bad output.
+#
+# To test output, you need to create $TEST.ref
+# $TEST.ref should hold the correct output for running the program:
+#  - Everything between (not including) /^---START---$/ and /^---STOP---$/
+#  - But NOT including output from /^---begin SWITCHBACK/
+#    to /^---  end SWITCHBACK/ inclusive
+#
+
+######################################################
+# Global consts, vars
+use constant DEBUG => 0;
+use constant CONST_N_MAX => 100000000;
+use constant CONST_N_MUL => 2;
+
+my $SWITCHBACK = "./switchback";
+my $N_START = 0;
+my $N_LAST_GOOD = 0;
+my $N_LAST_BAD = -1;
+my $GIVEN_LAST_BAD = -1;
+my $TEST;
+
+
+
+######################################################
+# Helper functions
+
+sub Exit {
+    exit $_[0];
+}
+
+sub Usage {
+    print "Usage: binary_switchback.pl test_name <start> <last_good> <last_bad>\n";
+    print "where:\n";
+    print "   test_name = obj filename - without '.o'\n";
+    print "   last_good = last known good bb (search space minimum)\n";
+    print "   last_bad  = last known bad bb (search space maximum)\n";
+    print "\n";
+}
+
+sub QuitUsage {
+    print $_[0]."\n";
+    Usage();
+    Exit 1;
+}
+
+
+######################################################
+# Get & check cmdline args
+# - if given, override global vars.
+
+if (@ARGV < 1 || @ARGV > 3) {
+    QuitUsage "Error: Bad num args\n";
+}
+
+$TEST = $ARGV[0];
+
+if ( ! -e "$TEST.o" ) {
+    QuitUsage "File doesn't exist: '$TEST.o'\n";
+}
+
+if ( ! -x "$SWITCHBACK" ) {
+    QuitUsage "File doesn't exist | not executable: '$SWITCHBACK'\n";
+}
+
+if (@ARGV >1) {
+    $N_LAST_GOOD = $ARGV[1];
+    if (! ($N_LAST_GOOD =~ /^\d*$/)) {
+       QuitUsage "Error: bad arg for #last_good\n";
+    }
+    if ($N_LAST_GOOD >= CONST_N_MAX) {
+       QuitUsage "Error: #last_good >= N_MAX(".CONST_N_MAX.")\n";
+    }
+}
+if (@ARGV >2) {
+    $N_LAST_BAD = $ARGV[2];
+    $GIVEN_LAST_BAD = $N_LAST_BAD;
+    if (! ($N_LAST_BAD =~ /^\d*$/)) {
+       QuitUsage "Error: bad arg for 'last_bad'\n";
+    }
+}
+
+# Setup N_START
+if ($N_LAST_BAD != -1) {
+    # Start halfway:
+    my $diff = $N_LAST_BAD - $N_LAST_GOOD;
+    $N_START = $N_LAST_GOOD + ($diff - ($diff % 2)) / 2;
+} else {
+    # No known end: Start at beginning:
+    if ($N_LAST_GOOD > 0) {   # User-given last_good
+       $N_START = $N_LAST_GOOD;
+    } else {
+       $N_START = 100;       # Some reasonable number.
+    }
+}
+
+######################################################
+# Sanity checks (shouldn't ever happen)
+
+if ($N_START < $N_LAST_GOOD) {
+    print "Program Error: start < last_good\n";
+    exit 1;
+}
+if ($N_LAST_BAD != -1 && $N_START >= $N_LAST_BAD) {
+    print "Program Error: start >= last_bad\n";
+    exit 1;
+}
+if ($N_START < 1 || $N_START > CONST_N_MAX) {
+    print "Program Error: Bad N_START: '$N_START'\n";
+    exit 1;
+}
+if ($N_LAST_GOOD < 0 || $N_LAST_GOOD > CONST_N_MAX) {
+    print "Program Error: Bad N_LAST_GOOD: '$N_LAST_GOOD'\n";
+    exit 1;
+}
+if ($N_LAST_BAD < -1 || $N_LAST_BAD > CONST_N_MAX) {
+    print "Program Error: Bad N_LAST_BAD: '$N_LAST_BAD'\n";
+    exit 1;
+}
+
+
+
+
+
+
+######################################################
+# Helper functions
+
+# Run switchback for test, for N bbs
+# returns output results
+sub SwitchBack {
+    my $n = $_[0];
+    if ($n < 0 || $n > CONST_N_MAX) {
+       print "Error SwitchBack: Bad N: '$n'\n";
+       Exit 1;
+    }
+    my $TMPFILE = ".switchback_output.$n";
+
+    print "=== Calling switchback for basic block $n ===\n";
+
+    system("$SWITCHBACK $TEST.o 0 $n >& $TMPFILE");
+    my $ret = $?;
+
+    if ($ret & 127) {
+       print "Ctrl-C pressed - Quitting...\n";
+       exit 0;
+    }
+
+    if (DEBUG) {
+       if ($ret == -1) {
+           print "failed to execute: $!\n";
+       }
+       elsif ($ret & 127) {
+           printf "child died with signal %d, %s coredump\n",
+            ($ret & 127),  ($ret & 128) ? 'with' : 'without';
+       }
+       else {
+           printf "child exited with value %d\n", $ret >> 8;
+       }
+    }
+    if ($ret != 0) { # Err: maybe seg fault
+       return;
+    }
+
+    open(INFILE, "$TMPFILE");
+    my @results = <INFILE>;
+    close(INFILE);
+
+    if (! DEBUG) {
+       unlink($TMPFILE);
+    }
+
+    return @results;
+}
+
+# Returns N simulated bbs from output lines
+sub get_N_simulated {
+    my @lines = @{$_[0]};
+    pop @lines;             # not the first...
+    my $line = pop @lines;  # ...but the second line.
+
+    chomp $line;
+    my $n;
+    if (($n) = ($line =~ /^(\d*) bbs simulated$/)) {
+       return $n;
+    }
+    print "Error: Didn't find N bbs simultated, from output lines\n";
+    Exit 1;
+}
+
+# Calls test script to compare current output lines with a reference.
+# Returns 1 on success, 0 on failure
+sub TestOutput {
+    my @lines = @{$_[0]};
+    my $n = $_[1];
+    my $ref_output = "$TEST.ref";
+
+    # Get the current section we want to compare:
+    my @newlines;
+    my $ok=0;
+    my $halfline = "";
+    foreach my $line(@lines) {
+       chomp $line;
+       if ($line =~ /^---STOP---$/) { last; }     # we're done
+
+       # output might be messed up here...
+       if ($line =~ /^.*---begin SWITCHBACK/) {
+           ($halfline) = ($line =~ /^(.*)---begin SWITCHBACK/);
+           $ok = 0;  # stop on prev line
+       }
+
+       # A valid line:
+       if ($ok) {
+           if ($halfline ne "") {   # Fix broken line
+               $line = $halfline.$line;
+               $halfline = "";
+           }
+           push(@newlines, $line);
+       }
+
+       if ($line =~ /^---START---$/) {            # start on next line
+           $ok = 1;
+       }
+
+       if ($line =~ /^---  end SWITCHBACK/) {     # start on next line
+           $ok = 1;
+           
+       }
+    }
+
+    if (DEBUG) {
+       open(OUTFILE, ">.filtered_output.$n");
+       print OUTFILE join("\n",@newlines);
+       close(OUTFILE);
+    }
+
+    # Read in reference lines
+    open(REFERENCE, "$ref_output") || die "Error: Couldn't open $ref_output\n";
+    my @ref_lines = <REFERENCE>;
+    close(REFERENCE);
+
+    # Compare reference lines with current:
+    my $match = 1;
+    my $i = 0;
+    foreach my $ref_line(@ref_lines) {
+       chomp $ref_line;
+       my $line = $newlines[$i++];
+       chomp $line;
+       if ($ref_line ne $line) {
+           print "\nMismatch on output:\n";
+           print "ref: '$ref_line'\n";
+           print "new: '$line'\n\n";
+           $match = 0;
+           last;
+       }
+    }
+    return $match;
+}
+
+
+
+
+
+
+######################################################
+# Do the search
+
+if (DEBUG) {
+    print "\n------------\n";
+    print "START:  N=$N_START\n";
+    print "START: lg=$N_LAST_GOOD\n";
+    print "START: lb=$N_LAST_BAD\n";
+    print "START: GIVEN_LAST_BAD=$GIVEN_LAST_BAD\n";
+    print "\n";
+}
+
+my $N = $N_START;
+my $success = 0;
+my @sb_output;
+while (1) {
+    if (DEBUG) {
+       print "\n------------\n";
+       print "SOL: lg=$N_LAST_GOOD\n";
+       print "SOL: lb=$N_LAST_BAD\n";
+       print "SOL:  N=$N\n";
+    }
+    if ($N < 0) {
+       print "Error: $N<0\n";
+       Exit 1;
+    }
+
+    my $ok = 1;
+    # Run switchback:
+    @sb_output = SwitchBack($N);
+
+    if (@sb_output == 0) { # Switchback failed - maybe seg fault
+       $ok = 0;
+    }
+
+    if (DEBUG) {
+       open(fileOUT, ">.retrieved_output.$N") or die("Can't open file for writing: $!");
+       print fileOUT @sb_output;
+       close(fileOUT);
+    }
+
+    # If we're ok so far (no seg faults) then test for correct output
+    if ($ok) {
+       $ok = TestOutput( \@sb_output, $N );
+    }
+
+    if ($ok) {
+       if (get_N_simulated(\@sb_output) < $N) { # Done: No bad bbs
+           $success = 1;
+           last;
+       }
+       if ($N_LAST_BAD == -1) {
+           # No upper bound for search space
+           # Try again with a bigger N
+
+           $N_LAST_GOOD = $N;
+           $N *= CONST_N_MUL;
+           if ($N > CONST_N_MAX) {
+               print "\nError: Maxed out N($N): N_MAX=".CONST_N_MAX."\n";
+               print "\nWe're either in a loop, or this is a big test program (increase N_MAX)\n\n";
+               Exit 1;
+           }
+           print "Looks good so far: Trying bigger N...\n\n";
+           next;
+       }
+    }
+
+    # Narrow the search space:
+    if ($ok) { $N_LAST_GOOD = $N; }
+    else {     $N_LAST_BAD  = $N;  }
+
+    # Calculate next step:
+    my $diff = $N_LAST_BAD - $N_LAST_GOOD;
+    $diff = $diff - ($diff % 2);
+    my $step = $diff / 2;
+
+    if ($step < 0) {
+       print "Error: step = $step\n";
+       Exit 1;
+    }
+
+    # This our last run-through?
+    if ($step!=0) {
+       $N = $N_LAST_GOOD + $step;   # Keep on going...
+    } else {
+       last;                        # Get outta here
+    }
+
+    if (DEBUG) {
+       print "\nEOL: ok=$ok\n";
+       print "EOL: lg=$N_LAST_GOOD\n";
+       print "EOL: lb=$N_LAST_BAD\n";
+       print "EOL:  s=$step\n";
+       print "EOL:  N=$N\n";
+    }
+}
+
+
+
+######################################################
+# Done: Report results
+
+print "\n============================================\n";
+print "Done searching.\n\n";
+
+if ($N_LAST_BAD != -1 && $N != $N_LAST_BAD) {
+    print "Get output for last bad bb:\n";
+    @sb_output = SwitchBack($N_LAST_BAD);
+}
+
+print @sb_output;
+print "\n\n";
+if ($success) {
+    print "*** Success!  No bad bbs found. ***\n";
+} else {
+    if ($N_LAST_BAD == $GIVEN_LAST_BAD) {
+       print "*** No failures detected within given bb range ***\n";
+       print " - check given 'last_bad' argument\n";
+    } else {
+       print "*** Failure: Last failed switchback bb: $N_LAST_BAD ***\n";
+       print "Hence bad bb: ". ($N_LAST_BAD - 1) ."\n";
+    }
+}
+print "\n";
+if (DEBUG) {
+    print "END:  N=$N\n";
+    print "END: lg=$N_LAST_GOOD\n";
+    print "END: lb=$N_LAST_BAD\n";
+    print "END: GIVEN_LAST_BAD=$GIVEN_LAST_BAD\n";
+    print "\n";
+}
+Exit 0;