diff options
Diffstat (limited to 'datalab-handout/driver.pl')
| -rwxr-xr-x | datalab-handout/driver.pl | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/datalab-handout/driver.pl b/datalab-handout/driver.pl new file mode 100755 index 0000000..143c440 --- /dev/null +++ b/datalab-handout/driver.pl @@ -0,0 +1,439 @@ +#!/usr/bin/perl +####################################################################### +# driver.pl - CS:APP Data Lab driver +# +# Copyright (c) 2004-2011, R. Bryant and D. O'Hallaron, All rights +# reserved. May not be used, modified, or copied without permission. +# +# Note: The driver can use either btest or the BDD checker to check +# puzzles for correctness. This version of the lab uses btest, which +# has been extended to do better testing of both integer and +# floating-point puzzles. +# +####################################################################### + +use strict 'vars'; +use Getopt::Std; + +use lib "."; +use Driverlib; + +# Set to 1 to use btest, 0 to use the BDD checker. +my $USE_BTEST = 1; + +# Generic settings +$| = 1; # Flush stdout each time +umask(0077); # Files created by the user in tmp readable only by that user +$ENV{PATH} = "/usr/local/bin:/usr/bin:/bin"; + +# +# usage - print help message and terminate +# +sub usage { + printf STDERR "$_[0]\n"; + printf STDERR "Usage: $0 [-h] [-u \"nickname\"]\n"; + printf STDERR "Options:\n"; + printf STDERR " -h Print this message.\n"; + printf STDERR " -u \"nickname\" Send autoresult to server, using nickname on scoreboard)\n"; + die "\n"; +} + +############## +# Main routine +############## +my $login = getlogin() || (getpwuid($<))[0] || "unknown"; +my $tmpdir = "/var/tmp/datalab.$login.$$"; +my $diemsg = "The files are in $tmpdir."; + +my $driverfiles; +my $infile; +my $autograded; + +my $status; +my $inpuzzles; +my $puzzlecnt; +my $line; +my $blank; +my $name; +my $c_points; +my $c_rating; +my $c_errors; +my $p_points; +my $p_rating; +my $p_errors; +my $total_c_points; +my $total_c_rating; +my $total_p_points; +my $total_p_rating; +my $tops; +my $tpoints; +my $trating; +my $foo; +my $name; +my $msg; +my $nickname; +my $autoresult; + +my %puzzle_c_points; +my %puzzle_c_rating; +my %puzzle_c_errors; +my %puzzle_p_points; +my %puzzle_p_ops; +my %puzzle_p_maxops; +my %puzzle_number; + + +# Parse the command line arguments +no strict; +getopts('hu:f:A'); +if ($opt_h) { + usage(); +} + +# The default input file is bits.c (change with -f) +$infile = "bits.c"; +$nickname = ""; + +##### +# These are command line args that every driver must support +# + +# Causes the driver to send an autoresult to the server on behalf of user +if ($opt_u) { + $nickname = $opt_u; + check_nickname($nickname); +} + +# Hidden flag that indicates that the driver was invoked by an autograder +if ($opt_A) { + $autograded = $opt_A; +} + +##### +# Drivers can also define an arbitary number of other command line args +# +# Optional hidden flag used by the autograder +if ($opt_f) { + $infile = $opt_f; +} + +use strict 'vars'; + +################################################ +# Compute the correctness and performance scores +################################################ + +# Make sure that an executable dlc (data lab compiler) exists +(-e "./dlc" and -x "./dlc") + or die "$0: ERROR: No executable dlc binary.\n"; + + +# If using the bdd checker, then make sure it exists +if (!$USE_BTEST) { + (-e "./bddcheck/cbit/cbit" and -x "./bddcheck/cbit/cbit") + or die "$0: ERROR: No executable cbit binary.\n"; +} + +# +# Set up the contents of the scratch directory +# +system("mkdir $tmpdir") == 0 + or die "$0: Could not make scratch directory $tmpdir.\n"; + +# Copy the student's work to the scratch directory +unless (system("cp $infile $tmpdir/bits.c") == 0) { + clean($tmpdir); + die "$0: Could not copy file $infile to scratch directory $tmpdir.\n"; +} + +# Copy the various autograding files to the scratch directory +if ($USE_BTEST) { + $driverfiles = "Makefile dlc btest.c decl.c tests.c btest.h bits.h"; + unless (system("cp -r $driverfiles $tmpdir") == 0) { + clean($tmpdir); + die "$0: Could not copy autogradingfiles to $tmpdir.\n"; + } +} +else { + $driverfiles = "dlc tests.c bddcheck"; + unless (system("cp -r $driverfiles $tmpdir") == 0) { + clean($tmpdir); + die "$0: Could not copy support files to $tmpdir.\n"; + } +} + +# Change the current working directory to the scratch directory +unless (chdir($tmpdir)) { + clean($tmpdir); + die "$0: Could not change directory to $tmpdir.\n"; +} + +# +# Generate a zapped (for coding rules) version of bits.c. In this +# zapped version of bits.c, any functions with illegal operators are +# transformed to have empty function bodies. +# +print "1. Running './dlc -z' to identify coding rules violations.\n"; +system("cp bits.c save-bits.c") == 0 + or die "$0: ERROR: Could not create backup copy of bits.c. $diemsg\n"; +system("./dlc -z -o zap-bits.c bits.c") == 0 + or die "$0: ERROR: zapped bits.c did not compile. $diemsg\n"; + +# +# Run btest or BDD checker to determine correctness score +# +if ($USE_BTEST) { + print "\n2. Compiling and running './btest -g' to determine correctness score.\n"; + system("cp zap-bits.c bits.c"); + + # Compile btest + system("make btestexplicit") == 0 + or die "$0: Could not make btest in $tmpdir. $diemsg\n"; + + # Run btest + $status = system("./btest -g > btest-zapped.out 2>&1"); + if ($status != 0) { + die "$0: ERROR: btest check failed. $diemsg\n"; + } +} +else { + print "\n2. Running './bddcheck/check.pl -g' to determine correctness score.\n"; + system("cp zap-bits.c bits.c"); + $status = system("./bddcheck/check.pl -g > btest-zapped.out 2>&1"); + if ($status != 0) { + die "$0: ERROR: BDD check failed. $diemsg\n"; + } +} + +# +# Run dlc to identify operator count violations. +# +print "\n3. Running './dlc -Z' to identify operator count violations.\n"; +system("./dlc -Z -o Zap-bits.c save-bits.c") == 0 + or die "$0: ERROR: dlc unable to generated Zapped bits.c file.\n"; + +# +# Run btest or the bdd checker to compute performance score +# +if ($USE_BTEST) { + print "\n4. Compiling and running './btest -g -r 2' to determine performance score.\n"; + system("cp Zap-bits.c bits.c"); + + # Compile btest + system("make btestexplicit") == 0 + or die "$0: Could not make btest in $tmpdir. $diemsg\n"; + print "\n"; + + # Run btest + $status = system("./btest -g -r 2 > btest-Zapped.out 2>&1"); + if ($status != 0) { + die "$0: ERROR: Zapped btest failed. $diemsg\n"; + } +} +else { + print "\n4. Running './bddcheck/check.pl -g -r 2' to determine performance score.\n"; + system("cp Zap-bits.c bits.c"); + $status = system("./bddcheck/check.pl -g -r 2 > btest-Zapped.out 2>&1"); + if ($status != 0) { + die "$0: ERROR: Zapped bdd checker failed. $diemsg\n"; + } +} + +# +# Run dlc to get the operator counts on the zapped input file +# +print "\n5. Running './dlc -e' to get operator count of each function.\n"; +$status = system("./dlc -W1 -e zap-bits.c > dlc-opcount.out 2>&1"); +if ($status != 0) { + die "$0: ERROR: bits.c did not compile. $diemsg\n"; +} + +################################################################# +# Collect the correctness and performance results for each puzzle +################################################################# + +# +# Collect the correctness results +# +%puzzle_c_points = (); # Correctness score computed by btest +%puzzle_c_errors = (); # Correctness error discovered by btest +%puzzle_c_rating = (); # Correctness puzzle rating (max points) + +$inpuzzles = 0; # Becomes true when we start reading puzzle results +$puzzlecnt = 0; # Each puzzle gets a unique number +$total_c_points = 0; +$total_c_rating = 0; + +open(INFILE, "$tmpdir/btest-zapped.out") + or die "$0: ERROR: could not open input file $tmpdir/btest-zapped.out\n"; + +while ($line = <INFILE>) { + chomp($line); + + # Notice that we're ready to read the puzzle scores + if ($line =~ /^Score/) { + $inpuzzles = 1; + next; + } + + # Notice that we're through reading the puzzle scores + if ($line =~ /^Total/) { + $inpuzzles = 0; + next; + } + + # Read and record a puzzle's name and score + if ($inpuzzles) { + ($blank, $c_points, $c_rating, $c_errors, $name) = split(/\s+/, $line); + $puzzle_c_points{$name} = $c_points; + $puzzle_c_errors{$name} = $c_errors; + $puzzle_c_rating{$name} = $c_rating; + $puzzle_number{$name} = $puzzlecnt++; + $total_c_points += $c_points; + $total_c_rating += $c_rating; + } + +} +close(INFILE); + +# +# Collect the performance results +# +%puzzle_p_points = (); # Performance points + +$inpuzzles = 0; # Becomes true when we start reading puzzle results +$total_p_points = 0; +$total_p_rating = 0; + +open(INFILE, "$tmpdir/btest-Zapped.out") + or die "$0: ERROR: could not open input file $tmpdir/btest-Zapped.out\n"; + +while ($line = <INFILE>) { + chomp($line); + + # Notice that we're ready to read the puzzle scores + if ($line =~ /^Score/) { + $inpuzzles = 1; + next; + } + + # Notice that we're through reading the puzzle scores + if ($line =~ /^Total/) { + $inpuzzles = 0; + next; + } + + # Read and record a puzzle's name and score + if ($inpuzzles) { + ($blank, $p_points, $p_rating, $p_errors, $name) = split(/\s+/, $line); + $puzzle_p_points{$name} = $p_points; + $total_p_points += $p_points; + $total_p_rating += $p_rating; + } +} +close(INFILE); + +# +# Collect the operator counts generated by dlc +# +open(INFILE, "$tmpdir/dlc-opcount.out") + or die "$0: ERROR: could not open input file $tmpdir/dlc-opcount.out\n"; + +$tops = 0; +while ($line = <INFILE>) { + chomp($line); + + if ($line =~ /(\d+) operators/) { + ($foo, $foo, $foo, $name, $msg) = split(/:/, $line); + $puzzle_p_ops{$name} = $1; + $tops += $1; + } +} +close(INFILE); + +# +# Print a table of results sorted by puzzle number +# +print "\n"; +printf("%s\t%s\n", "Correctness Results", "Perf Results"); +printf("%s\t%s\t%s\t%s\t%s\t%s\n", "Points", "Rating", "Errors", + "Points", "Ops", "Puzzle"); +foreach $name (sort {$puzzle_number{$a} <=> $puzzle_number{$b}} + keys %puzzle_number) { + printf("%d\t%d\t%d\t%d\t%d\t\%s\n", + $puzzle_c_points{$name}, + $puzzle_c_rating{$name}, + $puzzle_c_errors{$name}, + $puzzle_p_points{$name}, + $puzzle_p_ops{$name}, + $name); +} + +$tpoints = $total_c_points + $total_p_points; +$trating = $total_c_rating + $total_p_rating; + +print "\nScore = $tpoints/$trating [$total_c_points/$total_c_rating Corr + $total_p_points/$total_p_rating Perf] ($tops total operators)\n"; + +# +# Optionally send the autoresult to the contest server if the driver +# was called with the -u command line flag. +# +if ($nickname) { + # Generate the autoresult + $autoresult = "$tpoints|$total_c_points|$total_p_points|$tops"; + foreach $name (sort {$puzzle_number{$a} <=> $puzzle_number{$b}} + keys %puzzle_number) { + $autoresult .= " |$name:$puzzle_c_points{$name}:$puzzle_c_rating{$name}:$puzzle_p_points{$name}:$puzzle_p_ops{$name}"; + } + + # Post the autoresult to the server. The Linux login id is + # concatenated with the user-supplied nickname for some (very) loose + # authentication of submissions. + &Driverlib::driver_post("$login:$nickname", $autoresult, $autograded); +} + +# Clean up and exit +clean ($tmpdir); +exit; + +################## +# Helper functions +# + +# +# check_nickname - Check a nickname for legality +# +sub check_nickname { + my $nickname = shift; + + # Nicknames can't be empty + if (length($nickname) < 1) { + die "$0: Error: Empty nickname.\n"; + } + + # Nicknames can't be too long + if (length($nickname) > 35) { + die "$0: Error: Nickname exceeds 35 characters.\n"; + } + + # Nicknames can have restricted set of metacharacters (e.g., no # + # HTML tags) + if (!($nickname =~ /^[_-\w.,'@ ]+$/)) { + die "$0: Error: Illegal character in nickname. Only alphanumerics, apostrophes, commas, periods, dashes, underscores, and ampersands are allowed.\n"; + } + + # Nicknames can't be all whitespace + if ($nickname =~ /^\s*$/) { + die "$0: Error: Nickname is all whitespace.\n"; + } + +} + +# +# clean - remove the scratch directory +# +sub clean { + my $tmpdir = shift; + system("rm -rf $tmpdir"); +} + |
