}
# Default set of tests.
-@TESTS = ("alarm-single", "alarm-multiple", "alarm-zero", "alarm-negative",
- "join-simple",
- "join-quick", "join-multiple", "join-nested",
- "join-dummy", "join-invalid", "join-no",
- "priority-preempt", "priority-fifo", "priority-donate-one",
- "priority-donate-multiple", "priority-donate-nest",
- "mlfqs-on", "mlfqs-off")
+@TESTS = qw (create-normal create-empty create-null
+ create-long)
unless @TESTS > 0;
# Handle final grade mode.
# Extract submission.
extract_tarball () if ! -d "pintos";
+# Compile submission.
+compile ();
+
# Verify that the proper directory was submitted.
-d "pintos/src/threads" or die "pintos/src/threads: stat: $!\n";
$result = grade_test ($test);
$result =~ s/\n$//;
}
- print "$result\n";
+ print "$result";
+ print " - with warnings" if $result eq 'ok' && defined $details{$test};
+ print "\n";
$result{$test} = $result;
}
-# MLFQS takes results from mlfqs-on and mlfqs-off.
-grade_mlfqs_speedup ();
-grade_mlfqs_priority ();
-
# Write output.
write_grades ();
write_details ();
sub test_source {
my ($test) = @_;
my ($src) = "$GRADES_DIR/$test.c";
- $src = "$GRADES_DIR/mlfqs.c" if $test =~ /^mlfqs/;
-e $src or die "$src: stat: $!\n";
return $src;
}
sub test_constants {
my ($defines) = "";
- $defines .= "#define MLFQS 1\n" if $test eq 'mlfqs-on';
return $defines;
}
return $status;
}
+sub compile {
+ print "Compiling...\n";
+ xsystem ("cd pintos/src/userprog && make", LOG => "make")
+ or return "compile error";
+}
+
sub really_run_test {
# Need to run it.
# If there's residue from an earlier test, move it to .old.
# Make output directory.
mkdir "output/$test";
-
- # Change constants.h if necessary.
- my ($defines) = test_constants ($test);
- if ($defines ne snarf ("pintos/src/constants.h")) {
- open (CONSTANTS, ">pintos/src/constants.h");
- print CONSTANTS $defines;
- close (CONSTANTS);
- }
-
- # Changes devices/timer.c if necessary.
- my ($new_time_slice) = $test eq 'priority-fifo' ? 100 : 1;
- my (@timer) = snarf ("pintos/src/devices/timer.c");
- if (!grep (/^\#define TIME_SLICE $new_time_slice$/, @timer)) {
- @timer = grep (!/^\#define TIME_SLICE/, @timer);
- unshift (@timer, "#define TIME_SLICE $new_time_slice");
- open (TIMER, ">pintos/src/devices/timer.c");
- print TIMER map ("$_\n", @timer);
- close (TIMER);
- }
-
- # Copy in the new test.c and delete enough files to ensure a full rebuild.
- my ($src) = test_source ($test);
- xsystem ("cp $src pintos/src/threads/test.c", DIE => "cp failed\n");
- unlink ("pintos/src/threads/build/threads/test.o");
- unlink ("pintos/src/threads/build/kernel.o");
- unlink ("pintos/src/threads/build/kernel.bin");
- unlink ("pintos/src/threads/build/os.dsk");
-
- # Build.
- xsystem ("cd pintos/src/threads && make", LOG => "$test/make")
- or return "compile error";
-
- # Copy out files for backtraces later.
- xsystem ("cp pintos/src/threads/build/kernel.o output/$test");
- xsystem ("cp pintos/src/threads/build/os.dsk output/$test");
+ xsystem ("cp $GRADES_DIR/$test.dsk output/$test/fs.dsk",
+ DIE => "cp failed\n");
# Run.
my ($timeout) = 10;
- $timeout = 600 if $test =~ /^mlfqs/;
- xsystem ("cd pintos/src/threads/build && pintos -v run -q",
+ xsystem ("pintos "
+ . "--os-disk=pintos/src/userprog/build/os.dsk "
+ . "--fs-disk=output/$test/fs.dsk "
+ . "-v run -q -ex \"$test\"",
LOG => "$test/run",
TIMEOUT => $timeout)
or return "Bochs error";
return "ok";
}
\f
-sub grade_alarm_single {
- verify_alarm (1, @_);
+sub grade_create_empty {
+ my (@output) = @_;
+ verify_common (@_);
+ compare_output (["(create-empty) begin"], @output);
+
}
sub grade_alarm_multiple {
if !grep (/Powering off/, @output);
}
-sub compare_output {
- my ($exp_file, @actual) = @_;
- my (@expected) = snarf ($exp_file);
+sub eq_lines {
+ my ($actual, $expected) = @_;
+ return $actual eq $expected;
+}
+sub compare_output {
+ my ($exp, @actual) = @_;
@actual = map ("$_\n", @actual);
- @expected = map ("$_\n", @expected);
# Trim header and trailer from @actual.
- while (scalar (@actual) && $actual[0] ne $expected[0]) {
- shift (@actual);
+ our ($test);
+ my ($first);
+ for ($first = 0; $first <= $#actual; $first++) {
+ $first++, last if $actual[$first] =~ /^Executing '$test':$/;
}
- die "First line of expected output was not present.\n" if !@actual;
- while (scalar (@actual) && $actual[$#actual] ne $expected[$#expected]) {
- pop (@actual);
+
+ my ($last);
+ for ($last = $#actual; $last >= 0; $last--) {
+ $last--, last if $actual[$last] =~ /^Timer: \d+ ticks$/;
}
- die "Final line of expected output was not present.\n" if !@actual;
-
- # Check whether they're the same.
- if ($#actual == $#expected) {
- my ($eq) = 1;
- for (my ($i) = 0; $i <= $#expected; $i++) {
- $eq = 0 if $actual[$i] ne $expected[$i];
- }
- return if $eq;
+
+ if ($last < $first) {
+ my ($no_first) = $first > $#actual;
+ my ($no_last) = $last < $#actual;
+ die "Couldn't locate output.\n";
}
- # They differ. Output a diff.
- my (@diff) = "";
- my ($d) = Algorithm::Diff->new (\@expected, \@actual);
- while ($d->Next ()) {
- my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2));
- if ($d->Same ()) {
- push (@diff, map (" $_", $d->Items (1)));
- } else {
- push (@diff, map ("- $_", $d->Items (1))) if $d->Items (1);
- push (@diff, map ("+ $_", $d->Items (2))) if $d->Items (2);
+ @actual = @actual[$first ... $last];
+
+ # Fix up lines that look like exit codes.
+ for my $i (0...$#actual) {
+ if (my ($process, $code)
+ = $actual[$i] =~ /^([-a-zA-Z0-9 ]+):.*[ \(](-?\d+)\b\)?$/) {
+ $process = substr ($process, 0, 15);
+ $actual[$i] = "$process: exit($code)\n";
}
}
my ($details) = "";
$details .= "$test actual output:\n";
$details .= join ('', map (" $_", @actual));
- $details .= "\n$test expected output:\n";
- $details .= join ('', map (" $_", @expected));
- $details .= "\nOutput differences in `diff -u' format:\n";
- $details .= join ('', @diff) . "\n";
+
+ my ($fuzzy_match) = 0;
+ for (my ($i) = 0; ; $i++) {
+ my ($fn) = $exp;
+ $fn .= $i if $i;
+ if (! -e $fn) {
+ die "$exp: stat: $!\n" if !$i;
+ last;
+ }
+ my (@expected) = map ("$_\n", snarf ($fn));
+
+ $details .= "\n$test acceptable output:\n";
+ $details .= join ('', map (" $_", @expected));
+
+ # Check whether they're the same.
+ if ($#actual == $#expected) {
+ my ($eq) = 1;
+ for (my ($i) = 0; $i <= $#expected; $i++) {
+ $eq = 0 if !eq_lines ($actual[$i], $expected[$i]);
+ }
+ return if $eq;
+ }
+
+ # They differ. Output a diff.
+ my (@diff) = "";
+ my ($d) = Algorithm::Diff->new (\@expected, \@actual);
+ my ($all_additions) = 1;
+ while ($d->Next ()) {
+ my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2));
+ if ($d->Same ()) {
+ push (@diff, map (" $_", $d->Items (1)));
+ } else {
+ push (@diff, map ("- $_", $d->Items (1))) if $d->Items (1);
+ push (@diff, map ("+ $_", $d->Items (2))) if $d->Items (2);
+ $all_additions = 0 if $d->Items (1);
+ }
+ }
+
+ $fuzzy_match = 1 if $all_additions;
+
+ $details .= "Differences in `diff -u' format:\n";
+ $details .= join ('', @diff);
+ $details .= "(This is considered a `fuzzy match'.)\n" if $fuzzy_match;
+ }
+
$details{$test} = $details;
- die "Output differs from expected. Details at end of file.\n";
+ die "Output differs from expected. Details at end of file.\n"
+ unless $fuzzy_match;
}
\f
sub write_grades {
open (DETAILS, ">details.out");
my ($n) = 0;
for my $test (@TESTS) {
- next if $result{$test} eq 'ok';
+ next if $result{$test} eq 'ok' && !defined $details{$test};
my ($details) = $details{$test};
next if !defined ($details) && ! -e "output/$test/run.out";
unlink ("output/$log.err") if defined ($log) && $status == 0;
+ die $options{DIE} if $status != 0 && defined $options{DIE};
+
return $status == 0;
}
---------------------
Argument passing
- -5 args-argc: argc is not set correctly
- -5 args-argv0: executable name not passed as argv[0]
- -5 args-multiple: passing multiple arguments fails
- -5 args-dblspace: using multiple spaces between arguments fails
+ -3 args-argc: argc is not set correctly
+ -3 args-argv0: executable name not passed as argv[0]
+ -3 args-multiple: passing multiple arguments fails
+ -3 args-dblspace: using multiple spaces between arguments fails
+Score: /12
+
+System calls
+ -3 syscall-bad-sp: system call with a bad stack pointer must not crash OS
+ -3 syscall-bad-arg: syscall with argument off top of stack must not crash OS
+ -3 syscall-boundary: syscall with args across page boundary must work
+Score: /9
System calls: halt, exec
-1 halt: halt system call fails
-1 create-bad-ptr: pass invalid pointer to create system call
-1 create-long: pass long file name to create system call
-1 create-exists: pass name of an existing file to create system call
+ -1 create-boundary: pass name of file crossing page boundary
Score: /7
System calls: open
-2 open-normal: open a file in the most normal way
-2 open-missing: try to open a file that doesn't exist
+ -2 open-boundary: pass name of file crossing page boundary
-1 open-empty: pass empty string to open system call
-1 open-null: pass null pointer to open system call
-1 open-twice: open the same file twice
System calls: read
-2 read-normal: read from open file in most normal way
-2 read-bad-ptr: pass invalid pointer to read system call
+ -2 read-boundary: pass buffer crossing page boundary
-1 read-zero: try to read zero bytes
-1 read-stdout: try to read from stdout
-1 read-bad-fd: try to read from invalid file descriptor
System calls: write
-2 write-normal: write to open file in most normal way
-2 write-bad-ptr: pass invalid pointer to write system call
+ -2 write-boundary: pass buffer crossing page boundary
-1 write-zero: try to write zero bytes
-1 write-stdin: try to write to stdin
-1 write-bad-fd: try to write to invalid file descriptor
Score: /7
Multiprogramming
- -
-
-3. Multiprogramming / Page boundary checking (/50 points)
- Multiprogramming: Exec'ing two simple programs -30
- from another program doesn't work
- Open/Create fails on page boundary -5
- Read/Write fails on page boundary -5
- Can close file opened by parent -5
- Running out of physical memory crashes OS -5
- Shell test doesn't work with normal page size -5
- Shell test doesn't work with smaller page size -5
- Exec/Join tests 0 and 1 fail when we substitute a new
- bitmap.cc that returns pages in a random order. -5
-
------------
-Notes on the shell tests :
-
-Large page size echo/matmult test
- -5 if nothing works no matter what you do
- -4 if shell works in either case but matmult doesn't work in either case
-
-Small page size echo/matmult test
- -5 if nothing works no matter what you do
- -4 if shell works in either case but matmult doesn't work in either case
-------------
-
+ -2 multi-recurse: test recursively executing subprocesses
+ -2 multi-oom: exhausting user memory must not crash OS
+ -2 multi-fragment: programs must load even if user memory is fragmented
+ -2 multi-parent-fd: child must not be able to close parent's fds
+Score: /8