When a Pintos run times out, report the load average.
[pintos-anon] / grading / lib / Pintos / Grading.pm
index 3411b637dc7ad69a8096afe25755b672bcf56d0c..bf6099f74e9540eb8fe468ae2c44a099085b8ac2 100644 (file)
@@ -13,26 +13,38 @@ our $action;
 our $hw;
 
 use POSIX;
-use Getopt::Long;
+use Getopt::Long qw(:config no_ignore_case);
 use Algorithm::Diff;
 \f
+# We execute lots of subprocesses.
+# Without this, our stdout output can get flushed multiple times,
+# which is harmless but looks bizarre.
+$| = 1;
+
 sub parse_cmd_line {
+    my ($do_regex, $no_regex);
     GetOptions ("v|verbose+" => \$verbose,
                "h|help" => sub { usage (0) },
-               "T|tests=s" => \@TESTS,
+               "d|do-tests=s" => \$do_regex,
+               "n|no-tests=s" => \$no_regex,
                "c|clean" => sub { set_action ('clean'); },
                "x|extract" => sub { set_action ('extract'); },
                "b|build" => sub { set_action ('build'); },
                "t|test" => sub { set_action ('test'); },
                "a|assemble" => sub { set_action ('assemble'); })
        or die "Malformed command line; use --help for help.\n";
-    die "Non-option argument not supported; use --help for help.\n"
+    die "Non-option arguments not supported; use --help for help.\n"
        if @ARGV > 0;
     @TESTS = split(/,/, join (',', @TESTS)) if defined @TESTS;
 
     if (!defined $action) {
        $action = -e 'review.txt' ? 'assemble' : 'test';
     }
+
+    my (@default_tests) = @_;
+    @TESTS = @default_tests;
+    @TESTS = grep (/$do_regex/, @TESTS) if defined $do_regex;
+    @TESTS = grep (!/$no_regex/, @TESTS) if defined $no_regex;
 }
 
 sub set_action {
@@ -70,7 +82,8 @@ Workflow:
 
 Options:
   -c, --clean        Delete test results and temporary files, then exit.
-  -T, --tests=TESTS  Run only the specified comma-separated tests.
+  -d, --do-tests=RE  Run only tests that match the given regular expression.
+  -n, --no-tests=RE  Do not run tests that match the given regular expression.
   -x, --extract      Stop after step 1.
   -b, --build        Stop after step 2.
   -t, --test         Stop after step 3 (default if "review.txt" not present).
@@ -87,11 +100,12 @@ EOF
 # applies any patches providing in the grading directory,
 # and installs a default pintos/src/constants.h
 sub extract_sources {
-    # Nothing to do if we already have a source tree.
-    return if -d "pintos";
-
+    # Make sure the output dir exists.
     -d ("output") || mkdir ("output") or die "output: mkdir: $!\n";
 
+    # Nothing else to do if we already have a source tree.
+    return if -d "pintos";
+
     my ($tarball) = choose_tarball ();
 
     # Extract sources.
@@ -185,7 +199,7 @@ sub run_and_grade_tests {
            if $grade eq 'ok' && defined $details{$test};
        print "$msg\n";
        
-       $result{$test} = $result;
+       $result{$test} = $grade;
     }
 }
 
@@ -331,12 +345,9 @@ sub xsystem {
            print "Child terminated with signal $signal\n";
        }
 
-       my ($exp_status) = !defined ($options{EXPECT}) ? 0 : $options{EXPECT};
-       $result = WIFEXITED ($status) && WEXITSTATUS ($status) == $exp_status
-           ? "ok" : "error";
+       $result = $status == 0 ? "ok" : "error";
     }
 
-
     if ($result eq 'error' && defined $options{DIE}) {
        my ($msg) = $options{DIE};
        if (defined ($log)) {
@@ -388,10 +399,14 @@ sub get_test_result {
 
 sub run_pintos {
     my ($cmd_line, %args) = @_;
-    $args{EXPECT} = 1 unless defined $args{EXPECT};
     my ($retval) = xsystem ($cmd_line, %args);
     return 'ok' if $retval eq 'ok';
-    return "Timed out after $args{TIMEOUT} seconds" if $retval eq 'timeout';
+    if ($retval eq 'timeout') {
+       my ($msg) = "Timed out after $args{TIMEOUT} seconds";
+       my ($load_avg) = `uptime` =~ /(load average:.*)$/i;
+       $msg .= " - $load_avg" if defined $load_avg;
+       return $msg;
+    }
     return 'Error running Bochs' if $retval eq 'error';
     die;
 }
@@ -399,7 +414,9 @@ sub run_pintos {
 # Grade the test.
 sub grade_test {
     # Read test output.
-    my (@output) = snarf ("output/$test/run.out");
+    my ($outfile) = "output/$test/run.out";
+    die "$outfile: missing test output file (make failed?)" if ! -e $outfile;
+    my (@output) = snarf ($outfile);
 
     # If there's a function "grade_$test", use it to evaluate the output.
     # If there's a file "$GRADES_DIR/$test.exp", compare its contents
@@ -539,7 +556,7 @@ sub look_for_panic {
        }
        my ($kernel_o);
        if ($hw eq 'threads') {
-           $kernel_o = "pintos/src/filesys/build/kernel.o";
+           $kernel_o = "output/$test/kernel.o";
        } else {
            $kernel_o = "pintos/src/$hw/build/kernel.o";
        }
@@ -816,5 +833,12 @@ sub file_contains {
     close (FILE);
     return $equal;
 }
+\f
+sub success {
+    for my $test (@TESTS) {
+       return 1 if !defined ($result{$test}) || $result{$test} ne 'ok';
+    }
+    return 0;
+}
 
 1;