3 # Find the directory that contains the grading files.
6 # Add our Perl library directory to the include path.
8 ($GRADES_DIR = $0) =~ s#/[^/]+$##;
9 -d $GRADES_DIR or die "$GRADES_DIR: stat: $!\n";
10 unshift @INC, "$GRADES_DIR/../lib";
17 our ($hw) = "threads";
18 our (@TESTS); # Tests to run.
24 parse_cmd_line qw (alarm-single alarm-multiple alarm-zero alarm-negative
25 priority-preempt priority-fifo priority-donate-one
26 priority-donate-multiple priority-donate-nest
29 clean_dir (), exit if $action eq 'clean';
32 exit if $action eq 'extract';
35 exit if $action eq 'build';
37 run_and_grade_tests ();
38 if (defined ($result{'mlfqs-on'}) && defined ($result{'mlfqs-off'})) {
39 grade_mlfqs_speedup ();
40 grade_mlfqs_priority ();
44 exit success () if $action eq 'test';
46 assemble_final_grade ();
47 exit success () if $action eq 'assemble';
49 die "Don't know how to '$action'";
51 # Runs $test in directory output/$test.
52 # Returns 'ok' if it went ok, otherwise an explanation.
54 # Change constants.h if necessary.
55 my ($defines) = $test ne 'mlfqs-on' ? "" : "#define MLFQS 1\n";
56 if ($defines ne snarf ("pintos/src/constants.h")) {
57 open (CONSTANTS, ">pintos/src/constants.h");
58 print CONSTANTS $defines;
62 # Changes devices/timer.c if necessary.
63 my ($new_time_slice) = $test eq 'priority-fifo' ? 100 : 1;
64 my (@timer_c) = snarf ("pintos/src/devices/timer.c");
65 if (!grep (/^\#define TIME_SLICE $new_time_slice$/, @timer_c)) {
66 @timer_c = grep (!/^\#define TIME_SLICE/, @timer_c);
67 unshift (@timer_c, "#define TIME_SLICE $new_time_slice");
68 open (TIMER_C, ">pintos/src/devices/timer.c");
69 print TIMER_C map ("$_\n", @timer_c);
73 # Changes devices/timer.h if necessary.
74 my ($new_timer_freq) = $test eq 'priority-fifo' ? 19 : 100;
75 my (@timer_h) = snarf ("pintos/src/devices/timer.h");
76 if (!grep (/^\#define TIMER_FREQ $new_time_slice$/, @timer_h)) {
77 @timer_h = grep (!/^\#define TIMER_FREQ/, @timer_h);
78 unshift (@timer_h, "#define TIMER_FREQ $new_timer_freq");
79 open (TIMER_H, ">pintos/src/devices/timer.h");
80 print TIMER_H map ("$_\n", @timer_h);
84 # Copy in the new test.c and delete enough files to ensure a full rebuild.
85 my ($src) = "$GRADES_DIR/" . ($test !~ /^mlfqs/ ? "$test.c" : "mlfqs.c");
86 -e $src or die "$src: stat: $!\n";
87 xsystem ("cp $src pintos/src/threads/test.c", DIE => "cp failed\n");
88 unlink ("pintos/src/threads/build/threads/test.o");
89 unlink ("pintos/src/threads/build/kernel.o");
90 unlink ("pintos/src/threads/build/kernel.bin");
91 unlink ("pintos/src/threads/build/os.dsk");
94 if (xsystem ("cd pintos/src/threads && make",
95 LOG => "$test/make") ne 'ok') {
96 $details{$test} = snarf ("output/$test/make.err");
97 return "Compile error";
100 # Copy out files for backtraces later.
101 xsystem ("cp pintos/src/threads/build/kernel.o output/$test");
102 xsystem ("cp pintos/src/threads/build/os.dsk output/$test");
105 my ($timeout) = $test !~ /^mlfqs/ ? 15 : 600;
106 return run_pintos (["-v", "run", "-q"],
107 CHDIR => "pintos/src/threads/build",
109 TIMEOUT => $timeout);
112 sub grade_alarm_single {
113 verify_alarm (1, @_);
116 sub grade_alarm_multiple {
117 verify_alarm (7, @_);
121 my ($iterations, @output) = @_;
123 verify_common (@output);
126 for (my ($i) = 0; $i < $iterations; $i++) {
127 for (my ($t) = 0; $t < 5; $t++) {
128 push (@products, ($i + 1) * ($t + 1) * 10);
131 @products = sort {$a <=> $b} @products;
135 die $_ if /out of order/i;
137 my ($p) = /product=(\d+)$/;
140 my ($q) = shift (@products);
141 die "Too many wakeups.\n" if !defined $q;
142 die "Out of order wakeups ($p vs. $q).\n" if $p != $q; # FIXME
144 die scalar (@products) . " fewer wakeups than expected.\n"
148 sub grade_alarm_zero {
150 verify_common (@output);
151 die "Crashed in timer_sleep()\n" if !grep (/^Success\.$/, @output);
154 sub grade_alarm_negative {
156 verify_common (@output);
157 die "Crashed in timer_sleep()\n" if !grep (/^Success\.$/, @output);
160 sub grade_priority_fifo {
163 verify_common (@output);
164 my ($thread_cnt) = 10;
167 my (@t) = (-1) x $thread_cnt;
170 my ($idx) = /^Thread (\d+)/ or next;
171 my ($iter) = /iteration (\d+)$/;
172 $iter = $iter_cnt if /done!$/;
173 die "Malformed output\n" if !defined $iter;
174 if (@order < $thread_cnt) {
176 die "Thread $idx repeated within first $thread_cnt iterations: "
177 . join (' ', @order) . ".\n"
178 if grep ($_ == $idx, @order) != 1;
180 die "Thread $idx ran when $order[0] should have.\n"
181 if $idx != $order[0];
182 push (@order, shift @order);
184 die "Thread $idx out of order output.\n" if $t[$idx] != $iter - 1;
189 for my $idx (0..$#t) {
190 if ($t[$idx] == -1) {
191 $err .= "Thread $idx did not run at all.\n";
192 } elsif ($t[$idx] != $iter_cnt) {
193 $err .= "Thread $idx only completed $t[$idx] iterations.\n";
196 die $err if $err ne '';
201 verify_common (@output);
202 our (@mlfqs_on_stats) = mlfqs_stats (@output);
205 sub grade_mlfqs_off {
207 verify_common (@output);
208 our (@mlfqs_off_stats) = mlfqs_stats (@output);
211 sub grade_mlfqs_speedup {
212 our (@mlfqs_off_stats);
213 our (@mlfqs_on_stats);
216 my ($off_ticks) = $mlfqs_off_stats[1];
217 my ($on_ticks) = $mlfqs_on_stats[1];
218 die "$off_ticks ticks without MLFQS, $on_ticks with MLFQS\n"
219 if $on_ticks >= $off_ticks;
223 $result{'mlfqs-speedup'} = $@;
226 sub grade_mlfqs_priority {
227 our (@mlfqs_off_stats);
228 our (@mlfqs_on_stats);
230 check_mlfqs () if !defined (@mlfqs_on_stats);
231 for my $cat qw (CPU IO MIX) {
232 die "Priority changed away from PRI_DEFAULT (29) without MLFQS\n"
233 if $mlfqs_off_stats[0]{$cat}{MIN} != 29
234 || $mlfqs_off_stats[0]{$cat}{MAX} != 29;
235 die "Minimum priority never changed from PRI_DEFAULT (29) "
237 if $mlfqs_on_stats[0]{$cat}{MIN} == 29;
238 die "Maximum priority never changed from PRI_DEFAULT (29) "
240 if $mlfqs_on_stats[0]{$cat}{MAX} == 29;
245 $result{'mlfqs-priority'} = $@;
249 our (@mlfqs_off_stats);
250 our (@mlfqs_on_stats);
251 die "p1-4 didn't finish with MLFQS on or off\n"
252 if !defined (@mlfqs_off_stats) && !defined (@mlfqs_on_stats);
253 die "p1-4 didn't finish with MLFQS on\n"
254 if !defined (@mlfqs_on_stats);
255 die "p1-4 didn't finish with MLFQS off\n"
256 if !defined (@mlfqs_off_stats);
261 my (%stats) = (CPU => {}, IO => {}, MIX => {});
262 my (%map) = ("CPU intensive" => 'CPU',
263 "IO intensive" => 'IO',
264 "Alternating IO/CPU" => 'MIX');
265 my (%rmap) = reverse %map;
269 $ticks = $1 if /Timer: (\d+) ticks/;
270 my ($thread, $pri) = /^([A-Za-z\/ ]+): (\d+)$/ or next;
271 my ($t) = $map{$thread} or next;
273 my ($s) = $stats{$t};
276 $$s{SUM2} += $pri * $pri;
277 $$s{MIN} = $pri if !defined ($$s{MIN}) || $pri < $$s{MIN};
278 $$s{MAX} = $pri if !defined ($$s{MAX}) || $pri > $$s{MAX};
281 my (%expect_n) = (CPU => 5000, IO => 1000, MIX => 12000);
282 for my $cat (values (%map)) {
283 my ($s) = $stats{$cat};
284 die "$rmap{$cat} printed $$s{N} times, not $expect_n{$cat}\n"
285 if $$s{N} != $expect_n{$cat};
286 die "$rmap{$cat} priority dropped to $$s{MIN}, below PRI_MIN (0)\n"
288 die "$rmap{$cat} priority rose to $$s{MAX}, above PRI_MAX (59)\n"
290 $$s{MEAN} = $$s{SUM} / $$s{N};
293 return (\%stats, $ticks);