provided code

This commit is contained in:
LabTS
2024-10-01 23:37:39 +01:00
commit 8724a2641e
697 changed files with 74252 additions and 0 deletions

1713
src/tests/Algorithm/Diff.pm Normal file

File diff suppressed because it is too large Load Diff

75
src/tests/Make.tests Normal file
View File

@@ -0,0 +1,75 @@
# -*- makefile -*-
include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS))
PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS))
TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS))
EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES))
OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES))
ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES))
RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES))
ifdef PROGS
include ../../Makefile.userprog
endif
TIMEOUT = 60
clean::
rm -f $(OUTPUTS) $(ERRORS) $(RESULTS)
grade:: results
$(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@
check:: results
@cat $<
@COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ ]//g;'`"; \
FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ ]//g;'`"; \
if [ $$FAILURES = 0 ]; then \
echo "All $$COUNT tests passed!"; \
else \
echo "Warning: $$FAILURES of $$COUNT tests failed!"; \
fi
results: $(RESULTS)
@for d in $(TESTS) $(EXTRA_GRADES); do \
if echo PASS | cmp -s $$d.result -; then \
echo "pass $$d"; \
else \
echo "FAIL $$d"; \
fi; \
done > $@
outputs:: $(OUTPUTS)
$(foreach prog,$(PROGS),$(eval $(prog).output: $(prog)))
$(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES)))
$(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test)))
# Prevent an environment variable VERBOSE from surprising us.
VERBOSE =
TESTCMD = pintos -v -k -T $(TIMEOUT)
TESTCMD += $(SIMULATOR)
TESTCMD += $(PINTOSOPTS)
ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
TESTCMD += $(FILESYSSOURCE)
TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file)))
endif
ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
TESTCMD += --swap-size=8
endif
TESTCMD += -- -q
TESTCMD += $(KERNELFLAGS)
ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
TESTCMD += -f
endif
TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F))
TESTCMD += < /dev/null
TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output
%.output: kernel.bin loader.bin
$(TESTCMD)
%.result: %.ck %.output
perl -I$(SRCDIR) $< $* $@

53
src/tests/arc4.c Normal file
View File

@@ -0,0 +1,53 @@
#include <stdint.h>
#include "tests/arc4.h"
/* Swap bytes. */
static inline void
swap_byte (uint8_t *a, uint8_t *b)
{
uint8_t t = *a;
*a = *b;
*b = t;
}
void
arc4_init (struct arc4 *arc4, const void *key_, size_t size)
{
const uint8_t *key = key_;
size_t key_idx;
uint8_t *s;
int i, j;
s = arc4->s;
arc4->i = arc4->j = 0;
for (i = 0; i < 256; i++)
s[i] = (uint8_t) i;
for (key_idx = 0, i = j = 0; i < 256; i++)
{
j = (j + s[i] + key[key_idx]) & 255;
swap_byte (s + i, s + j);
if (++key_idx >= size)
key_idx = 0;
}
}
void
arc4_crypt (struct arc4 *arc4, void *buf_, size_t size)
{
uint8_t *buf = buf_;
uint8_t *s;
uint8_t i, j;
s = arc4->s;
i = arc4->i;
j = arc4->j;
while (size-- > 0)
{
i = (uint8_t) (i + 1);
j = (uint8_t) (j + s[i]);
swap_byte (s + i, s + j);
*buf++ ^= s[(s[i] + s[j]) & 255];
}
arc4->i = i;
arc4->j = j;
}

17
src/tests/arc4.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef TESTS_ARC4_H
#define TESTS_ARC4_H
#include <stddef.h>
#include <stdint.h>
/* Alleged RC4 algorithm encryption state. */
struct arc4
{
uint8_t s[256];
uint8_t i, j;
};
void arc4_init (struct arc4 *, const void *, size_t);
void arc4_crypt (struct arc4 *, void *, size_t);
#endif /* tests/arc4.h */

29
src/tests/arc4.pm Normal file
View File

@@ -0,0 +1,29 @@
use strict;
use warnings;
sub arc4_init {
my ($key) = @_;
my (@s) = 0...255;
my ($j) = 0;
for my $i (0...255) {
$j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff;
@s[$i, $j] = @s[$j, $i];
}
return (0, 0, @s);
}
sub arc4_crypt {
my ($arc4, $buf) = @_;
my ($i, $j, @s) = @$arc4;
my ($out) = "";
for my $c (split (//, $buf)) {
$i = ($i + 1) & 0xff;
$j = ($j + $s[$i]) & 0xff;
@s[$i, $j] = @s[$j, $i];
$out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]);
}
@$arc4 = ($i, $j, @s);
return $out;
}
1;

92
src/tests/cksum.c Normal file
View File

@@ -0,0 +1,92 @@
/* crctab[] and cksum() are from the `cksum' entry in SUSv3. */
#include <stdint.h>
#include "tests/cksum.h"
static unsigned long crctab[] = {
0x00000000,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
/* This is the algorithm used by the Posix `cksum' utility. */
unsigned long
cksum (const void *b_, size_t n)
{
const unsigned char *b = b_;
uint32_t s = 0;
size_t i;
for (i = n; i > 0; --i)
{
unsigned char c = *b++;
s = (s << 8) ^ crctab[(s >> 24) ^ c];
}
while (n != 0)
{
unsigned char c = (unsigned char) n;
n >>= 8;
s = (s << 8) ^ crctab[(s >> 24) ^ c];
}
return ~s;
}
#ifdef STANDALONE_TEST
#include <stdio.h>
int
main (void)
{
char buf[65536];
int n = fread (buf, 1, sizeof buf, stdin);
printf ("%lu\n", cksum (buf, n));
return 0;
}
#endif

8
src/tests/cksum.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef TESTS_CKSUM_H
#define TESTS_CKSUM_H
#include <stddef.h>
unsigned long cksum(const void *, size_t);
#endif /* tests/cksum.h */

87
src/tests/cksum.pm Normal file
View File

@@ -0,0 +1,87 @@
# From the `cksum' entry in SUSv3.
use strict;
use warnings;
my (@crctab) =
(0x00000000,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4);
sub cksum {
my ($b) = @_;
my ($n) = length ($b);
my ($s) = 0;
for my $i (0...$n - 1) {
my ($c) = ord (substr ($b, $i, 1));
$s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
$s &= 0xffff_ffff;
}
while ($n != 0) {
my ($c) = $n & 0xff;
$n >>= 8;
$s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
$s &= 0xffff_ffff;
}
return ~$s & 0xffff_ffff;
}
sub cksum_file {
my ($file) = @_;
open (FILE, '<', $file) or die "$file: open: $!\n";
my ($data);
sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n";
close (FILE);
return cksum ($data);
}
1;

View File

@@ -0,0 +1,4 @@
# Percentage of the testing point total designated for each set of tests.
50.0% tests/devices/Rubric.alarmfunc
50.0% tests/devices/Rubric.alarmrobust

View File

@@ -0,0 +1,48 @@
# -*- makefile -*-
# Test names.
tests/devices_TESTS = $(addprefix tests/devices/,alarm-single \
alarm-multiple alarm-simultaneous alarm-no-busy-wait alarm-one \
alarm-zero alarm-negative)
# Sources for tests.
tests/devices_SRC = tests/devices/tests.c
tests/devices_SRC += tests/devices/alarm-wait.c
tests/devices_SRC += tests/devices/alarm-simultaneous.c
tests/devices_SRC += tests/devices/alarm-no-busy-wait.c
tests/devices_SRC += tests/devices/alarm-one.c
tests/devices_SRC += tests/devices/alarm-zero.c
tests/devices_SRC += tests/devices/alarm-negative.c

View File

@@ -0,0 +1,4 @@
Functionality of alarm clock:
10 alarm-no-busy-wait
5 alarm-single
5 alarm-multiple

View File

@@ -0,0 +1,5 @@
Robustness of alarm clock:
10 alarm-simultaneous
5 alarm-one
5 alarm-zero
5 alarm-negative

View File

@@ -0,0 +1,4 @@
# -*- perl -*-
use tests::tests;
use tests::devices::alarm;
check_alarm (7);

View File

@@ -0,0 +1,15 @@
/* Tests timer_sleep(-100). Only requirement is that it not crash. */
#include <stdio.h>
#include "tests/devices/tests.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
void
test_alarm_negative (void)
{
timer_sleep (-100);
pass ();
}

View File

@@ -0,0 +1,10 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected ([<<'EOF']);
(alarm-negative) begin
(alarm-negative) PASS
(alarm-negative) end
EOF
pass;

View File

@@ -0,0 +1,131 @@
/* Creates 5 threads, each of which sleeps for 20 ticks.
Checks to ensure that the threads are genuinely sleeping (not busy-waiting)
by inspecting the ready_list half-way though the sleep time.
*/
#include <stdio.h>
#include "tests/devices/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
static void test_sleep (int thread_cnt, int iterations);
void
test_alarm_no_busy_wait (void)
{
test_sleep (5, 1);
}
/* Information about the test. */
struct sleep_test
{
int64_t start; /* Current time at start of test. */
int iterations; /* Number of iterations per thread. */
/* Output. */
struct lock output_lock; /* Lock protecting output buffer. */
int *output_pos; /* Current position in output buffer. */
};
/* Information about an individual thread in the test. */
struct sleep_thread
{
struct sleep_test *test; /* Info shared between all threads. */
int id; /* Sleeper ID. */
int duration; /* Number of ticks to sleep. */
int iterations; /* Iterations counted so far. */
};
static void sleeper (void *);
/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
static void
test_sleep (int thread_cnt, int iterations)
{
struct sleep_test test;
struct sleep_thread *threads;
int *output;
int i;
/* This test does not work with the MLFQS. */
ASSERT (!thread_mlfqs);
msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
msg ("Each thread sleeps for 20 ticks at a time,");
msg ("Test is successful if the threads are not running");
msg ("when they are supposed to be asleep.");
/* Allocate memory. */
threads = malloc (sizeof *threads * (int)(thread_cnt));
output = malloc (sizeof *output * (int)(iterations * thread_cnt * 2));
if (threads == NULL || output == NULL)
PANIC ("couldn't allocate memory for test");
/* Initialize test. */
test.start = timer_ticks () + 100;
test.iterations = iterations;
lock_init (&test.output_lock);
test.output_pos = output;
/* Start threads. */
ASSERT (output != NULL);
for (i = 0; i < thread_cnt; i++)
{
struct sleep_thread *t = threads + i;
char name[16];
t->test = &test;
t->id = i;
t->duration = 20;
t->iterations = 0;
snprintf (name, sizeof name, "thread %d", i);
thread_create (name, PRI_DEFAULT, sleeper, t);
}
/* yield the CPU so that the new threads have sufficient time to go to sleep */
timer_sleep(10);
/* now check that all of the threads are indeed asleep */
/* Acquire the output lock so we cannot race with the sleeping threads */
lock_acquire (&test.output_lock);
/* Inspect and print the number of threads on the ready list . */
size_t num_ready_threads = threads_ready();
msg("%d threads on the ready list", num_ready_threads);
if (num_ready_threads > 0)
fail ("too many threads on the ready_list (they should all be asleep)!");
pass ();
lock_release (&test.output_lock);
/* Wait long enough for all the threads to finish. */
timer_sleep (100 + thread_cnt * iterations * 20 + 100);
/* Clean up after ourselves */
free (output);
free (threads);
}
/* Sleeper thread. */
static void
sleeper (void *t_)
{
struct sleep_thread *t = t_;
struct sleep_test *test = t->test;
int i;
for (i = 1; i <= test->iterations; i++)
{
int64_t sleep_until = test->start + i * t->duration;
timer_sleep (sleep_until - timer_ticks ());
lock_acquire (&test->output_lock);
*test->output_pos++ = t->id;
lock_release (&test->output_lock);
}
}

View File

@@ -0,0 +1,15 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected ([<<'EOF']);
(alarm-no-busy-wait) begin
(alarm-no-busy-wait) Creating 5 threads to sleep 1 times each.
(alarm-no-busy-wait) Each thread sleeps for 20 ticks at a time,
(alarm-no-busy-wait) Test is successful if the threads are not running
(alarm-no-busy-wait) when they are supposed to be asleep.
(alarm-no-busy-wait) 0 threads on the ready list
(alarm-no-busy-wait) PASS
(alarm-no-busy-wait) end
EOF
pass;

View File

@@ -0,0 +1,18 @@
/* Tests timer_sleep(1), which should return shortly after called.
This test can expose a race-condition if the kernel attempts to unblock
the thread before it has been blocked.
*/
#include <stdio.h>
#include "tests/devices/tests.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
void
test_alarm_one (void)
{
timer_sleep (1);
pass ();
}

View File

@@ -0,0 +1,10 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected ([<<'EOF']);
(alarm-one) begin
(alarm-one) PASS
(alarm-one) end
EOF
pass;

View File

@@ -0,0 +1,94 @@
/* Creates N threads, each of which sleeps a different, fixed
duration, M times. Records the wake-up order and verifies
that it is valid. */
#include <stdio.h>
#include "tests/devices/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
static void test_sleep (int thread_cnt, int iterations);
void
test_alarm_simultaneous (void)
{
test_sleep (3, 5);
}
/* Information about the test. */
struct sleep_test
{
int64_t start; /* Current time at start of test. */
int iterations; /* Number of iterations per thread. */
int *output_pos; /* Current position in output buffer. */
};
static void sleeper (void *);
/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
static void
test_sleep (int thread_cnt, int iterations)
{
struct sleep_test test;
int *output;
int i;
/* This test does not work with the MLFQS. */
ASSERT (!thread_mlfqs);
msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
msg ("Each thread sleeps 10 ticks each time.");
msg ("Within an iteration, all threads should wake up on the same tick.");
/* Allocate memory. */
output = malloc (sizeof *output * (int)(iterations * thread_cnt * 2));
if (output == NULL)
PANIC ("couldn't allocate memory for test");
/* Initialize test. */
test.start = timer_ticks () + 100;
test.iterations = iterations;
test.output_pos = output;
/* Start threads. */
ASSERT (output != NULL);
for (i = 0; i < thread_cnt; i++)
{
char name[16];
snprintf (name, sizeof name, "thread %d", i);
thread_create (name, PRI_DEFAULT, sleeper, &test);
}
/* Wait long enough for all the threads to finish. */
timer_sleep (100 + iterations * 10 + 100);
/* Print completion order. */
msg ("iteration 0, thread 0: woke up after %d ticks", output[0]);
for (i = 1; i < test.output_pos - output; i++)
msg ("iteration %d, thread %d: woke up %d ticks later",
i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]);
free (output);
}
/* Sleeper thread. */
static void
sleeper (void *test_)
{
struct sleep_test *test = test_;
int i;
/* Make sure we're at the beginning of a timer tick. */
timer_sleep (1);
for (i = 1; i <= test->iterations; i++)
{
int64_t sleep_until = test->start + i * 10;
timer_sleep (sleep_until - timer_ticks ());
*test->output_pos++ = (int)(timer_ticks () - test->start);
thread_yield ();
}
}

View File

@@ -0,0 +1,27 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected ([<<'EOF']);
(alarm-simultaneous) begin
(alarm-simultaneous) Creating 3 threads to sleep 5 times each.
(alarm-simultaneous) Each thread sleeps 10 ticks each time.
(alarm-simultaneous) Within an iteration, all threads should wake up on the same tick.
(alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks
(alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later
(alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later
(alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later
(alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later
(alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later
(alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later
(alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later
(alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later
(alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later
(alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later
(alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later
(alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later
(alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later
(alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later
(alarm-simultaneous) end
EOF
pass;

View File

@@ -0,0 +1,4 @@
# -*- perl -*-
use tests::tests;
use tests::devices::alarm;
check_alarm (1);

View File

@@ -0,0 +1,152 @@
/* Creates N threads, each of which sleeps a different, fixed
duration, M times. Records the wake-up order and verifies
that it is valid. */
#include <stdio.h>
#include "tests/devices/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
static void test_sleep (int thread_cnt, int iterations);
void
test_alarm_single (void)
{
test_sleep (5, 1);
}
void
test_alarm_multiple (void)
{
test_sleep (5, 7);
}
/* Information about the test. */
struct sleep_test
{
int64_t start; /* Current time at start of test. */
int iterations; /* Number of iterations per thread. */
/* Output. */
struct lock output_lock; /* Lock protecting output buffer. */
int *output_pos; /* Current position in output buffer. */
};
/* Information about an individual thread in the test. */
struct sleep_thread
{
struct sleep_test *test; /* Info shared between all threads. */
int id; /* Sleeper ID. */
int duration; /* Number of ticks to sleep. */
int iterations; /* Iterations counted so far. */
};
static void sleeper (void *);
/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
static void
test_sleep (int thread_cnt, int iterations)
{
struct sleep_test test;
struct sleep_thread *threads;
int *output, *op;
int product;
int i;
/* This test does not work with the MLFQS. */
ASSERT (!thread_mlfqs);
msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
msg ("Thread 0 sleeps 10 ticks each time,");
msg ("thread 1 sleeps 20 ticks each time, and so on.");
msg ("If successful, product of iteration count and");
msg ("sleep duration will appear in nondescending order.");
/* Allocate memory. */
threads = malloc (sizeof *threads * (int)(thread_cnt));
output = malloc (sizeof *output * (int)(iterations * thread_cnt * 2));
if (threads == NULL || output == NULL)
PANIC ("couldn't allocate memory for test");
/* Initialize test. */
test.start = timer_ticks () + 100;
test.iterations = iterations;
lock_init (&test.output_lock);
test.output_pos = output;
/* Start threads. */
ASSERT (output != NULL);
for (i = 0; i < thread_cnt; i++)
{
struct sleep_thread *t = threads + i;
char name[16];
t->test = &test;
t->id = i;
t->duration = (i + 1) * 10;
t->iterations = 0;
snprintf (name, sizeof name, "thread %d", i);
thread_create (name, PRI_DEFAULT, sleeper, t);
}
/* Wait long enough for all the threads to finish. */
timer_sleep (100 + thread_cnt * iterations * 10 + 100);
/* Acquire the output lock in case some rogue thread is still
running. */
lock_acquire (&test.output_lock);
/* Print completion order. */
product = 0;
for (op = output; op < test.output_pos; op++)
{
struct sleep_thread *t;
int new_prod;
ASSERT (*op >= 0 && *op < thread_cnt);
t = threads + *op;
new_prod = ++t->iterations * t->duration;
msg ("thread %d: duration=%d, iteration=%d, product=%d",
t->id, t->duration, t->iterations, new_prod);
if (new_prod >= product)
product = new_prod;
else
fail ("thread %d woke up out of order (%d > %d)!",
t->id, product, new_prod);
}
/* Verify that we had the proper number of wakeups. */
for (i = 0; i < thread_cnt; i++)
if (threads[i].iterations != iterations)
fail ("thread %d woke up %d times instead of %d",
i, threads[i].iterations, iterations);
lock_release (&test.output_lock);
free (output);
free (threads);
}
/* Sleeper thread. */
static void
sleeper (void *t_)
{
struct sleep_thread *t = t_;
struct sleep_test *test = t->test;
int i;
for (i = 1; i <= test->iterations; i++)
{
int64_t sleep_until = test->start + i * t->duration;
timer_sleep (sleep_until - timer_ticks ());
lock_acquire (&test->output_lock);
*test->output_pos++ = t->id;
lock_release (&test->output_lock);
}
}

View File

@@ -0,0 +1,15 @@
/* Tests timer_sleep(0), which should return immediately. */
#include <stdio.h>
#include "tests/devices/tests.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
void
test_alarm_zero (void)
{
timer_sleep (0);
pass ();
}

View File

@@ -0,0 +1,10 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected ([<<'EOF']);
(alarm-zero) begin
(alarm-zero) PASS
(alarm-zero) end
EOF
pass;

View File

@@ -0,0 +1,32 @@
sub check_alarm {
my ($iterations) = @_;
our ($test);
@output = read_text_file ("$test.output");
common_checks ("run", @output);
my (@products);
for (my ($i) = 0; $i < $iterations; $i++) {
for (my ($t) = 0; $t < 5; $t++) {
push (@products, ($i + 1) * ($t + 1) * 10);
}
}
@products = sort {$a <=> $b} @products;
local ($_);
foreach (@output) {
fail $_ if /out of order/i;
my ($p) = /product=(\d+)$/;
next if !defined $p;
my ($q) = shift (@products);
fail "Too many wakeups.\n" if !defined $q;
fail "Out of order wakeups ($p vs. $q).\n" if $p != $q;
}
fail scalar (@products) . " fewer wakeups than expected.\n"
if @products != 0;
pass;
}
1;

117
src/tests/devices/tests.c Normal file
View File

@@ -0,0 +1,117 @@
#include "tests/devices/tests.h"
#include <debug.h>
#include <string.h>
#include <stdio.h>
struct test
{
const char *name;
test_func *function;
};
#ifndef THREADS
static const struct test tests[] =
{
{"alarm-single", test_alarm_single},
{"alarm-multiple", test_alarm_multiple},
{"alarm-simultaneous", test_alarm_simultaneous},
{"alarm-no-busy-wait", test_alarm_no_busy_wait},
{"alarm-one", test_alarm_one},
{"alarm-zero", test_alarm_zero},
{"alarm-negative", test_alarm_negative}
};
#else
static const struct test tests[] =
{
{"alarm-single", test_alarm_single},
{"alarm-multiple", test_alarm_multiple},
{"alarm-simultaneous", test_alarm_simultaneous},
{"alarm-no-busy-wait", test_alarm_no_busy_wait},
{"alarm-one", test_alarm_one},
{"alarm-zero", test_alarm_zero},
{"alarm-negative", test_alarm_negative},
{"alarm-priority", test_alarm_priority},
{"priority-change", test_priority_change},
{"priority-donate-one", test_priority_donate_one},
{"priority-donate-multiple", test_priority_donate_multiple},
{"priority-donate-multiple2", test_priority_donate_multiple2},
{"priority-donate-nest", test_priority_donate_nest},
{"priority-donate-sema", test_priority_donate_sema},
{"priority-donate-lower", test_priority_donate_lower},
{"priority-donate-chain", test_priority_donate_chain},
{"priority-preservation", test_priority_preservation},
{"priority-fifo", test_priority_fifo},
{"priority-preempt", test_priority_preempt},
{"priority-sema", test_priority_sema},
{"priority-condvar", test_priority_condvar},
{"mlfqs-load-1", test_mlfqs_load_1},
{"mlfqs-load-60", test_mlfqs_load_60},
{"mlfqs-load-avg", test_mlfqs_load_avg},
{"mlfqs-recent-1", test_mlfqs_recent_1},
{"mlfqs-fair-2", test_mlfqs_fair_2},
{"mlfqs-fair-20", test_mlfqs_fair_20},
{"mlfqs-nice-2", test_mlfqs_nice_2},
{"mlfqs-nice-10", test_mlfqs_nice_10},
{"mlfqs-block", test_mlfqs_block},
};
#endif
static const char *test_name;
/* Runs the test named NAME. */
void
run_test (const char *name)
{
const struct test *t;
for (t = tests; t < tests + sizeof tests / sizeof *tests; t++)
if (!strcmp (name, t->name))
{
test_name = name;
msg ("begin");
t->function ();
msg ("end");
return;
}
PANIC ("no test named \"%s\"", name);
}
/* Prints FORMAT as if with printf(),
prefixing the output by the name of the test
and following it with a new-line character. */
void
msg (const char *format, ...)
{
va_list args;
printf ("(%s) ", test_name);
va_start (args, format);
vprintf (format, args);
va_end (args);
putchar ('\n');
}
/* Prints failure message FORMAT as if with printf(),
prefixing the output by the name of the test and FAIL:
and following it with a new-line character,
and then panics the kernel. */
void
fail (const char *format, ...)
{
va_list args;
printf ("(%s) FAIL: ", test_name);
va_start (args, format);
vprintf (format, args);
va_end (args);
putchar ('\n');
PANIC ("test failed");
}
/* Prints a message indicating the current test passed. */
void
pass (void)
{
printf ("(%s) PASS\n", test_name);
}

47
src/tests/devices/tests.h Normal file
View File

@@ -0,0 +1,47 @@
#ifndef TESTS_DEVICES_TESTS_H
#define TESTS_DEVICES_TESTS_H
void run_test (const char *);
typedef void test_func (void);
extern test_func test_alarm_single;
extern test_func test_alarm_multiple;
extern test_func test_alarm_simultaneous;
extern test_func test_alarm_no_busy_wait;
extern test_func test_alarm_one;
extern test_func test_alarm_zero;
extern test_func test_alarm_negative;
#ifdef THREADS
extern test_func test_alarm_priority;
extern test_func test_priority_change;
extern test_func test_priority_donate_one;
extern test_func test_priority_donate_multiple;
extern test_func test_priority_donate_multiple2;
extern test_func test_priority_donate_sema;
extern test_func test_priority_donate_nest;
extern test_func test_priority_donate_lower;
extern test_func test_priority_donate_chain;
extern test_func test_priority_preservation;
extern test_func test_priority_fifo;
extern test_func test_priority_preempt;
extern test_func test_priority_sema;
extern test_func test_priority_condvar;
extern test_func test_mlfqs_load_1;
extern test_func test_mlfqs_load_60;
extern test_func test_mlfqs_load_avg;
extern test_func test_mlfqs_recent_1;
extern test_func test_mlfqs_fair_2;
extern test_func test_mlfqs_fair_20;
extern test_func test_mlfqs_nice_2;
extern test_func test_mlfqs_nice_10;
extern test_func test_mlfqs_block;
#endif
void msg (const char *, ...);
void fail (const char *, ...);
void pass (void);
#endif /* tests/devices/tests.h */

View File

@@ -0,0 +1,18 @@
# Percentage of the testing point total designated for each set of
# tests.
# This task is primarily about implementing the file system, but
# all the previous functionality should work too. It's not too easy
# to screw it up, thus the emphasis.
# 65% for extended file system features.
30% tests/filesys/extended/Rubric.functionality
15% tests/filesys/extended/Rubric.robustness
20% tests/filesys/extended/Rubric.persistence
# 20% to not break the provided file system features.
20% tests/filesys/base/Rubric
# 15% for the rest.
10% tests/userprog/Rubric.functionality
5% tests/userprog/Rubric.robustness

View File

@@ -0,0 +1,22 @@
# Percentage of the testing point total designated for each set of
# tests.
# This task is primarily about implementing the file system, but
# all the previous functionality should work too. It's not too easy
# to screw it up, thus the emphasis.
# 65% for extended file system features.
30% tests/filesys/extended/Rubric.functionality
15% tests/filesys/extended/Rubric.robustness
20% tests/filesys/extended/Rubric.persistence
# 20% to not break the provided file system features.
20% tests/filesys/base/Rubric
# 15% for the rest.
10% tests/userprog/Rubric.functionality
5% tests/userprog/Rubric.robustness
# Up to 10% bonus for working VM functionality.
8% tests/vm/Rubric.functionality
2% tests/vm/Rubric.robustness

View File

@@ -0,0 +1,18 @@
# -*- makefile -*-
tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create \
lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full \
sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write)
tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix \
tests/filesys/base/,child-syn-read child-syn-wrt)
$(foreach prog,$(tests/filesys/base_PROGS), \
$(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
$(foreach prog,$(tests/filesys/base_TESTS), \
$(eval $(prog)_SRC += tests/main.c))
tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read
tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt
tests/filesys/base/syn-read.output: TIMEOUT = 300

View File

@@ -0,0 +1,19 @@
Functionality of base file system:
- Test basic support for small files.
1 sm-create
2 sm-full
2 sm-random
2 sm-seq-block
3 sm-seq-random
- Test basic support for large files.
1 lg-create
2 lg-full
2 lg-random
2 lg-seq-block
3 lg-seq-random
- Test synchronized multiprogram access to files.
4 syn-read
4 syn-write
2 syn-remove

View File

@@ -0,0 +1,44 @@
/* Child process for syn-read test.
Reads the contents of a test file a byte at a time, in the
hope that this will take long enough that we can get a
significant amount of contention in the kernel file system
code. */
#include <random.h>
#include <stdio.h>
#include <stdlib.h>
#include <syscall.h>
#include "tests/lib.h"
#include "tests/filesys/base/syn-read.h"
const char *test_name = "child-syn-read";
static char buf[BUF_SIZE];
int
main (int argc, const char *argv[])
{
int child_idx;
int fd;
size_t i;
quiet = true;
CHECK (argc == 2, "argc must be 2, actually %d", argc);
child_idx = atoi (argv[1]);
random_init (0);
random_bytes (buf, sizeof buf);
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
for (i = 0; i < sizeof buf; i++)
{
char c;
CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name);
compare_bytes (&c, buf + i, 1, i, file_name);
}
close (fd);
return child_idx;
}

View File

@@ -0,0 +1,35 @@
/* Child process for syn-read test.
Writes into part of a test file. Other processes will be
writing into other parts at the same time. */
#include <random.h>
#include <stdlib.h>
#include <syscall.h>
#include "tests/lib.h"
#include "tests/filesys/base/syn-write.h"
char buf[BUF_SIZE];
int
main (int argc, char *argv[])
{
int child_idx;
int fd;
quiet = true;
CHECK (argc == 2, "argc must be 2, actually %d", argc);
child_idx = atoi (argv[1]);
random_init (0);
random_bytes (buf, sizeof buf);
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
seek (fd, CHUNK_SIZE * (unsigned) child_idx);
CHECK (write (fd, buf + CHUNK_SIZE * (unsigned) child_idx, CHUNK_SIZE) > 0,
"write \"%s\"", file_name);
msg ("close \"%s\"", file_name);
close (fd);
return child_idx;
}

View File

@@ -0,0 +1,20 @@
/* -*- c -*- */
#include "tests/filesys/seq-test.h"
#include "tests/main.h"
static char buf[TEST_SIZE];
static size_t
return_test_size (void)
{
return TEST_SIZE;
}
void
test_main (void)
{
seq_test ("quux",
buf, sizeof buf, sizeof buf,
return_test_size, NULL);
}

View File

@@ -0,0 +1,5 @@
/* Tests that create properly zeros out the contents of a fairly
large file. */
#define TEST_SIZE 75678
#include "tests/filesys/create.inc"

View File

@@ -0,0 +1,13 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(lg-create) begin
(lg-create) create "blargle"
(lg-create) open "blargle" for verification
(lg-create) verified contents of "blargle"
(lg-create) close "blargle"
(lg-create) end
EOF
pass;

View File

@@ -0,0 +1,6 @@
/* Writes out the contents of a fairly large file all at once,
and then reads it back to make sure that it was written
properly. */
#define TEST_SIZE 75678
#include "tests/filesys/base/full.inc"

View File

@@ -0,0 +1,16 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(lg-full) begin
(lg-full) create "quux"
(lg-full) open "quux"
(lg-full) writing "quux"
(lg-full) close "quux"
(lg-full) open "quux" for verification
(lg-full) verified contents of "quux"
(lg-full) close "quux"
(lg-full) end
EOF
pass;

View File

@@ -0,0 +1,7 @@
/* Writes out the content of a fairly large file in random order,
then reads it back in random order to verify that it was
written properly. */
#define BLOCK_SIZE 512U
#define TEST_SIZE (512 * 150)
#include "tests/filesys/base/random.inc"

View File

@@ -0,0 +1,14 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(lg-random) begin
(lg-random) create "bazzle"
(lg-random) open "bazzle"
(lg-random) write "bazzle" in random order
(lg-random) read "bazzle" in random order
(lg-random) close "bazzle"
(lg-random) end
EOF
pass;

View File

@@ -0,0 +1,7 @@
/* Writes out a fairly large file sequentially, one fixed-size
block at a time, then reads it back to verify that it was
written properly. */
#define TEST_SIZE 75678
#define BLOCK_SIZE 513
#include "tests/filesys/base/seq-block.inc"

View File

@@ -0,0 +1,16 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(lg-seq-block) begin
(lg-seq-block) create "noodle"
(lg-seq-block) open "noodle"
(lg-seq-block) writing "noodle"
(lg-seq-block) close "noodle"
(lg-seq-block) open "noodle" for verification
(lg-seq-block) verified contents of "noodle"
(lg-seq-block) close "noodle"
(lg-seq-block) end
EOF
pass;

View File

@@ -0,0 +1,6 @@
/* Writes out a fairly large file sequentially, one random-sized
block at a time, then reads it back to verify that it was
written properly. */
#define TEST_SIZE 75678
#include "tests/filesys/base/seq-random.inc"

View File

@@ -0,0 +1,16 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(lg-seq-random) begin
(lg-seq-random) create "nibble"
(lg-seq-random) open "nibble"
(lg-seq-random) writing "nibble"
(lg-seq-random) close "nibble"
(lg-seq-random) open "nibble" for verification
(lg-seq-random) verified contents of "nibble"
(lg-seq-random) close "nibble"
(lg-seq-random) end
EOF
pass;

View File

@@ -0,0 +1,59 @@
/* -*- c -*- */
#include <random.h>
#include <stdio.h>
#include <string.h>
#include <syscall.h>
#include "tests/lib.h"
#include "tests/main.h"
#if TEST_SIZE % BLOCK_SIZE != 0
#error TEST_SIZE must be a multiple of BLOCK_SIZE
#endif
#define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE)
char buf[TEST_SIZE];
int order[BLOCK_CNT];
void
test_main (void)
{
const char *file_name = "bazzle";
int fd;
size_t i;
random_init (57);
random_bytes (buf, sizeof buf);
for (i = 0; i < BLOCK_CNT; i++)
order[i] = (int) i;
CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
msg ("write \"%s\" in random order", file_name);
shuffle (order, BLOCK_CNT, sizeof *order);
for (i = 0; i < BLOCK_CNT; i++)
{
size_t ofs = BLOCK_SIZE * (unsigned) order[i];
seek (fd, ofs);
if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE)
fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
}
msg ("read \"%s\" in random order", file_name);
shuffle (order, BLOCK_CNT, sizeof *order);
for (i = 0; i < BLOCK_CNT; i++)
{
char block[BLOCK_SIZE];
size_t ofs = BLOCK_SIZE * (unsigned) order[i];
seek (fd, ofs);
if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE)
fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name);
}
msg ("close \"%s\"", file_name);
close (fd);
}

View File

@@ -0,0 +1,20 @@
/* -*- c -*- */
#include "tests/filesys/seq-test.h"
#include "tests/main.h"
static char buf[TEST_SIZE];
static size_t
return_block_size (void)
{
return BLOCK_SIZE;
}
void
test_main (void)
{
seq_test ("noodle",
buf, sizeof buf, sizeof buf,
return_block_size, NULL);
}

View File

@@ -0,0 +1,22 @@
/* -*- c -*- */
#include <random.h>
#include "tests/filesys/seq-test.h"
#include "tests/main.h"
static char buf[TEST_SIZE];
static size_t
return_random (void)
{
return random_ulong () % 1031 + 1;
}
void
test_main (void)
{
random_init ( (unsigned) -1);
seq_test ("nibble",
buf, sizeof buf, sizeof buf,
return_random, NULL);
}

View File

@@ -0,0 +1,5 @@
/* Tests that create properly zeros out the contents of a fairly
small file. */
#define TEST_SIZE 5678
#include "tests/filesys/create.inc"

View File

@@ -0,0 +1,13 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(sm-create) begin
(sm-create) create "blargle"
(sm-create) open "blargle" for verification
(sm-create) verified contents of "blargle"
(sm-create) close "blargle"
(sm-create) end
EOF
pass;

View File

@@ -0,0 +1,6 @@
/* Writes out the contents of a fairly small file all at once,
and then reads it back to make sure that it was written
properly. */
#define TEST_SIZE 5678
#include "tests/filesys/base/full.inc"

View File

@@ -0,0 +1,16 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(sm-full) begin
(sm-full) create "quux"
(sm-full) open "quux"
(sm-full) writing "quux"
(sm-full) close "quux"
(sm-full) open "quux" for verification
(sm-full) verified contents of "quux"
(sm-full) close "quux"
(sm-full) end
EOF
pass;

View File

@@ -0,0 +1,7 @@
/* Writes out the content of a fairly small file in random order,
then reads it back in random order to verify that it was
written properly. */
#define BLOCK_SIZE 13
#define TEST_SIZE (13 * 123)
#include "tests/filesys/base/random.inc"

View File

@@ -0,0 +1,14 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(sm-random) begin
(sm-random) create "bazzle"
(sm-random) open "bazzle"
(sm-random) write "bazzle" in random order
(sm-random) read "bazzle" in random order
(sm-random) close "bazzle"
(sm-random) end
EOF
pass;

View File

@@ -0,0 +1,7 @@
/* Writes out a fairly small file sequentially, one fixed-size
block at a time, then reads it back to verify that it was
written properly. */
#define TEST_SIZE 5678
#define BLOCK_SIZE 513
#include "tests/filesys/base/seq-block.inc"

View File

@@ -0,0 +1,16 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(sm-seq-block) begin
(sm-seq-block) create "noodle"
(sm-seq-block) open "noodle"
(sm-seq-block) writing "noodle"
(sm-seq-block) close "noodle"
(sm-seq-block) open "noodle" for verification
(sm-seq-block) verified contents of "noodle"
(sm-seq-block) close "noodle"
(sm-seq-block) end
EOF
pass;

View File

@@ -0,0 +1,6 @@
/* Writes out a fairly large file sequentially, one random-sized
block at a time, then reads it back to verify that it was
written properly. */
#define TEST_SIZE 5678
#include "tests/filesys/base/seq-random.inc"

View File

@@ -0,0 +1,16 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(sm-seq-random) begin
(sm-seq-random) create "nibble"
(sm-seq-random) open "nibble"
(sm-seq-random) writing "nibble"
(sm-seq-random) close "nibble"
(sm-seq-random) open "nibble" for verification
(sm-seq-random) verified contents of "nibble"
(sm-seq-random) close "nibble"
(sm-seq-random) end
EOF
pass;

View File

@@ -0,0 +1,31 @@
/* Spawns 10 child processes, all of which read from the same
file and make sure that the contents are what they should
be. */
#include <random.h>
#include <stdio.h>
#include <syscall.h>
#include "tests/lib.h"
#include "tests/main.h"
#include "tests/filesys/base/syn-read.h"
static char buf[BUF_SIZE];
#define CHILD_CNT 10
void
test_main (void)
{
pid_t children[CHILD_CNT];
int fd;
CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name);
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
random_bytes (buf, sizeof buf);
CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name);
msg ("close \"%s\"", file_name);
close (fd);
exec_children ("child-syn-read", children, CHILD_CNT);
wait_children (children, CHILD_CNT);
}

View File

@@ -0,0 +1,33 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(syn-read) begin
(syn-read) create "data"
(syn-read) open "data"
(syn-read) write "data"
(syn-read) close "data"
(syn-read) exec child 1 of 10: "child-syn-read 0"
(syn-read) exec child 2 of 10: "child-syn-read 1"
(syn-read) exec child 3 of 10: "child-syn-read 2"
(syn-read) exec child 4 of 10: "child-syn-read 3"
(syn-read) exec child 5 of 10: "child-syn-read 4"
(syn-read) exec child 6 of 10: "child-syn-read 5"
(syn-read) exec child 7 of 10: "child-syn-read 6"
(syn-read) exec child 8 of 10: "child-syn-read 7"
(syn-read) exec child 9 of 10: "child-syn-read 8"
(syn-read) exec child 10 of 10: "child-syn-read 9"
(syn-read) wait for child 1 of 10 returned 0 (expected 0)
(syn-read) wait for child 2 of 10 returned 1 (expected 1)
(syn-read) wait for child 3 of 10 returned 2 (expected 2)
(syn-read) wait for child 4 of 10 returned 3 (expected 3)
(syn-read) wait for child 5 of 10 returned 4 (expected 4)
(syn-read) wait for child 6 of 10 returned 5 (expected 5)
(syn-read) wait for child 7 of 10 returned 6 (expected 6)
(syn-read) wait for child 8 of 10 returned 7 (expected 7)
(syn-read) wait for child 9 of 10 returned 8 (expected 8)
(syn-read) wait for child 10 of 10 returned 9 (expected 9)
(syn-read) end
EOF
pass;

View File

@@ -0,0 +1,7 @@
#ifndef TESTS_FILESYS_BASE_SYN_READ_H
#define TESTS_FILESYS_BASE_SYN_READ_H
#define BUF_SIZE 1024
static const char file_name[] = "data";
#endif /* tests/filesys/base/syn-read.h */

View File

@@ -0,0 +1,30 @@
/* Verifies that a deleted file may still be written to and read
from. */
#include <random.h>
#include <string.h>
#include <syscall.h>
#include "tests/lib.h"
#include "tests/main.h"
char buf1[1234];
char buf2[1234];
void
test_main (void)
{
const char *file_name = "deleteme";
int fd;
CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
CHECK (remove (file_name), "remove \"%s\"", file_name);
random_bytes (buf1, sizeof buf1);
CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name);
msg ("seek \"%s\" to 0", file_name);
seek (fd, 0);
CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name);
compare_bytes (buf2, buf1, sizeof buf1, 0, file_name);
msg ("close \"%s\"", file_name);
close (fd);
}

View File

@@ -0,0 +1,16 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(syn-remove) begin
(syn-remove) create "deleteme"
(syn-remove) open "deleteme"
(syn-remove) remove "deleteme"
(syn-remove) write "deleteme"
(syn-remove) seek "deleteme" to 0
(syn-remove) read "deleteme"
(syn-remove) close "deleteme"
(syn-remove) end
EOF
pass;

View File

@@ -0,0 +1,31 @@
/* Spawns several child processes to write out different parts of
the contents of a file and waits for them to finish. Then
reads back the file and verifies its contents. */
#include <random.h>
#include <stdio.h>
#include <string.h>
#include <syscall.h>
#include "tests/filesys/base/syn-write.h"
#include "tests/lib.h"
#include "tests/main.h"
char buf1[BUF_SIZE];
char buf2[BUF_SIZE];
void
test_main (void)
{
pid_t children[CHILD_CNT];
int fd;
CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
exec_children ("child-syn-wrt", children, CHILD_CNT);
wait_children (children, CHILD_CNT);
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name);
random_bytes (buf2, sizeof buf2);
compare_bytes (buf1, buf2, sizeof buf1, 0, file_name);
}

View File

@@ -0,0 +1,32 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
(syn-write) begin
(syn-write) create "stuff"
(syn-write) exec child 1 of 10: "child-syn-wrt 0"
(syn-write) exec child 2 of 10: "child-syn-wrt 1"
(syn-write) exec child 3 of 10: "child-syn-wrt 2"
(syn-write) exec child 4 of 10: "child-syn-wrt 3"
(syn-write) exec child 5 of 10: "child-syn-wrt 4"
(syn-write) exec child 6 of 10: "child-syn-wrt 5"
(syn-write) exec child 7 of 10: "child-syn-wrt 6"
(syn-write) exec child 8 of 10: "child-syn-wrt 7"
(syn-write) exec child 9 of 10: "child-syn-wrt 8"
(syn-write) exec child 10 of 10: "child-syn-wrt 9"
(syn-write) wait for child 1 of 10 returned 0 (expected 0)
(syn-write) wait for child 2 of 10 returned 1 (expected 1)
(syn-write) wait for child 3 of 10 returned 2 (expected 2)
(syn-write) wait for child 4 of 10 returned 3 (expected 3)
(syn-write) wait for child 5 of 10 returned 4 (expected 4)
(syn-write) wait for child 6 of 10 returned 5 (expected 5)
(syn-write) wait for child 7 of 10 returned 6 (expected 6)
(syn-write) wait for child 8 of 10 returned 7 (expected 7)
(syn-write) wait for child 9 of 10 returned 8 (expected 8)
(syn-write) wait for child 10 of 10 returned 9 (expected 9)
(syn-write) open "stuff"
(syn-write) read "stuff"
(syn-write) end
EOF
pass;

View File

@@ -0,0 +1,9 @@
#ifndef TESTS_FILESYS_BASE_SYN_WRITE_H
#define TESTS_FILESYS_BASE_SYN_WRITE_H
#define CHILD_CNT 10
#define CHUNK_SIZE 512U
#define BUF_SIZE (CHILD_CNT * CHUNK_SIZE)
static const char file_name[] = "stuff";
#endif /* tests/filesys/base/syn-write.h */

View File

@@ -0,0 +1,15 @@
/* -*- c -*- */
#include <syscall.h>
#include "tests/lib.h"
#include "tests/main.h"
static char buf[TEST_SIZE];
void
test_main (void)
{
const char *file_name = "blargle";
CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
check_file (file_name, buf, TEST_SIZE);
}

View File

@@ -0,0 +1,37 @@
#include "tests/filesys/seq-test.h"
#include <random.h>
#include <syscall.h>
#include "tests/lib.h"
void
seq_test (const char *file_name, void *buf, size_t size, size_t initial_size,
size_t (*block_size_func) (void),
void (*check_func) (int fd, long ofs))
{
size_t ofs;
int fd;
random_bytes (buf, size);
CHECK (create (file_name, initial_size), "create \"%s\"", file_name);
CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
ofs = 0;
msg ("writing \"%s\"", file_name);
while (ofs < size)
{
size_t block_size = block_size_func ();
if (block_size > size - ofs)
block_size = size - ofs;
if (write (fd, buf + ofs, block_size) != (int) block_size)
fail ("write %zu bytes at offset %zu in \"%s\" failed",
block_size, ofs, file_name);
ofs += block_size;
if (check_func != NULL)
check_func (fd, (long) ofs);
}
msg ("close \"%s\"", file_name);
close (fd);
check_file (file_name, buf, size);
}

View File

@@ -0,0 +1,11 @@
#ifndef TESTS_FILESYS_SEQ_TEST_H
#define TESTS_FILESYS_SEQ_TEST_H
#include <stddef.h>
void seq_test (const char *file_name,
void *buf, size_t size, size_t initial_size,
size_t (*block_size_func) (void),
void (*check_func) (int fd, long ofs));
#endif /* tests/filesys/seq-test.h */

174
src/tests/internal/list.c Normal file
View File

@@ -0,0 +1,174 @@
/* Test program for lib/kernel/list.c.
Attempts to test the list functionality that is not
sufficiently tested elsewhere in PintOS.
This is not a test we will run on your submitted tasks.
It is here for completeness.
*/
#undef NDEBUG
#include <debug.h>
#include <list.h>
#include <random.h>
#include <stdio.h>
#include "threads/test.h"
/* Maximum number of elements in a linked list that we will
test. */
#define MAX_SIZE 64
/* A linked list element. */
struct value
{
struct list_elem elem; /* List element. */
int value; /* Item value. */
};
static void shuffle (struct value[], size_t);
static bool value_less (const struct list_elem *, const struct list_elem *,
void *);
static void verify_list_fwd (struct list *, int size);
static void verify_list_bkwd (struct list *, int size);
/* Test the linked list implementation. */
void
test (void)
{
int size;
printf ("testing various size lists:");
for (size = 0; size < MAX_SIZE; size++)
{
int repeat;
printf (" %d", size);
for (repeat = 0; repeat < 10; repeat++)
{
static struct value values[MAX_SIZE * 4];
struct list list;
struct list_elem *e;
int i, ofs;
/* Put values 0...SIZE in random order in VALUES. */
for (i = 0; i < size; i++)
values[i].value = i;
shuffle (values, size);
/* Assemble list. */
list_init (&list);
for (i = 0; i < size; i++)
list_push_back (&list, &values[i].elem);
/* Verify correct minimum and maximum elements. */
e = list_min (&list, value_less, NULL);
ASSERT (size ? list_entry (e, struct value, elem)->value == 0
: e == list_begin (&list));
e = list_max (&list, value_less, NULL);
ASSERT (size ? list_entry (e, struct value, elem)->value == size - 1
: e == list_begin (&list));
/* Sort and verify list. */
list_sort (&list, value_less, NULL);
verify_list_fwd (&list, size);
/* Reverse and verify list. */
list_reverse (&list);
verify_list_bkwd (&list, size);
/* Shuffle, insert using list_insert_ordered(),
and verify ordering. */
shuffle (values, size);
list_init (&list);
for (i = 0; i < size; i++)
list_insert_ordered (&list, &values[i].elem,
value_less, NULL);
verify_list_fwd (&list, size);
/* Duplicate some items, uniquify, and verify. */
ofs = size;
for (e = list_begin (&list); e != list_end (&list);
e = list_next (e))
{
struct value *v = list_entry (e, struct value, elem);
int copies = random_ulong () % 4;
while (copies-- > 0)
{
values[ofs].value = v->value;
list_insert (e, &values[ofs++].elem);
}
}
ASSERT ((size_t) ofs < sizeof values / sizeof *values);
list_unique (&list, NULL, value_less, NULL);
verify_list_fwd (&list, size);
}
}
printf (" done\n");
printf ("list: PASS\n");
}
/* Shuffles the CNT elements in ARRAY into random order. */
static void
shuffle (struct value *array, size_t cnt)
{
size_t i;
for (i = 0; i < cnt; i++)
{
size_t j = i + random_ulong () % (cnt - i);
struct value t = array[j];
array[j] = array[i];
array[i] = t;
}
}
/* Returns true if value A is less than value B, false
otherwise. */
static bool
value_less (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED)
{
const struct value *a = list_entry (a_, struct value, elem);
const struct value *b = list_entry (b_, struct value, elem);
return a->value < b->value;
}
/* Verifies that LIST contains the values 0...SIZE when traversed
in forward order. */
static void
verify_list_fwd (struct list *list, int size)
{
struct list_elem *e;
int i;
for (i = 0, e = list_begin (list);
i < size && e != list_end (list);
i++, e = list_next (e))
{
struct value *v = list_entry (e, struct value, elem);
ASSERT (i == v->value);
}
ASSERT (i == size);
ASSERT (e == list_end (list));
}
/* Verifies that LIST contains the values 0...SIZE when traversed
in reverse order. */
static void
verify_list_bkwd (struct list *list, int size)
{
struct list_elem *e;
int i;
for (i = 0, e = list_rbegin (list);
i < size && e != list_rend (list);
i++, e = list_prev (e))
{
struct value *v = list_entry (e, struct value, elem);
ASSERT (i == v->value);
}
ASSERT (i == size);
ASSERT (e == list_rend (list));
}

208
src/tests/internal/stdio.c Normal file
View File

@@ -0,0 +1,208 @@
/* Test program for printf() in lib/stdio.c.
Attempts to test printf() functionality that is not
sufficiently tested elsewhere in PintOS.
This is not a test we will run on your submitted tasks.
It is here for completeness.
*/
#undef NDEBUG
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "threads/test.h"
/* Number of failures so far. */
static int failure_cnt;
static void
checkf (const char *expect, const char *format, ...)
{
char output[128];
va_list args;
printf ("\"%s\" -> \"%s\": ", format, expect);
va_start (args, format);
vsnprintf (output, sizeof output, format, args);
va_end (args);
if (strcmp (expect, output))
{
printf ("\nFAIL: actual output \"%s\"\n", output);
failure_cnt++;
}
else
printf ("okay\n");
}
/* Test printf() implementation. */
void
test (void)
{
printf ("Testing formats:");
/* Check that commas show up in the right places, for positive
numbers. */
checkf ("1", "%'d", 1);
checkf ("12", "%'d", 12);
checkf ("123", "%'d", 123);
checkf ("1,234", "%'d", 1234);
checkf ("12,345", "%'d", 12345);
checkf ("123,456", "%'ld", 123456L);
checkf ("1,234,567", "%'ld", 1234567L);
checkf ("12,345,678", "%'ld", 12345678L);
checkf ("123,456,789", "%'ld", 123456789L);
checkf ("1,234,567,890", "%'ld", 1234567890L);
checkf ("12,345,678,901", "%'lld", 12345678901LL);
checkf ("123,456,789,012", "%'lld", 123456789012LL);
checkf ("1,234,567,890,123", "%'lld", 1234567890123LL);
checkf ("12,345,678,901,234", "%'lld", 12345678901234LL);
checkf ("123,456,789,012,345", "%'lld", 123456789012345LL);
checkf ("1,234,567,890,123,456", "%'lld", 1234567890123456LL);
checkf ("12,345,678,901,234,567", "%'lld", 12345678901234567LL);
checkf ("123,456,789,012,345,678", "%'lld", 123456789012345678LL);
checkf ("1,234,567,890,123,456,789", "%'lld", 1234567890123456789LL);
/* Check that commas show up in the right places, for positive
numbers. */
checkf ("-1", "%'d", -1);
checkf ("-12", "%'d", -12);
checkf ("-123", "%'d", -123);
checkf ("-1,234", "%'d", -1234);
checkf ("-12,345", "%'d", -12345);
checkf ("-123,456", "%'ld", -123456L);
checkf ("-1,234,567", "%'ld", -1234567L);
checkf ("-12,345,678", "%'ld", -12345678L);
checkf ("-123,456,789", "%'ld", -123456789L);
checkf ("-1,234,567,890", "%'ld", -1234567890L);
checkf ("-12,345,678,901", "%'lld", -12345678901LL);
checkf ("-123,456,789,012", "%'lld", -123456789012LL);
checkf ("-1,234,567,890,123", "%'lld", -1234567890123LL);
checkf ("-12,345,678,901,234", "%'lld", -12345678901234LL);
checkf ("-123,456,789,012,345", "%'lld", -123456789012345LL);
checkf ("-1,234,567,890,123,456", "%'lld", -1234567890123456LL);
checkf ("-12,345,678,901,234,567", "%'lld", -12345678901234567LL);
checkf ("-123,456,789,012,345,678", "%'lld", -123456789012345678LL);
checkf ("-1,234,567,890,123,456,789", "%'lld", -1234567890123456789LL);
/* Check signed integer conversions. */
checkf (" 0", "%5d", 0);
checkf ("0 ", "%-5d", 0);
checkf (" +0", "%+5d", 0);
checkf ("+0 ", "%+-5d", 0);
checkf (" 0", "% 5d", 0);
checkf ("00000", "%05d", 0);
checkf (" ", "%5.0d", 0);
checkf (" 00", "%5.2d", 0);
checkf ("0", "%d", 0);
checkf (" 1", "%5d", 1);
checkf ("1 ", "%-5d", 1);
checkf (" +1", "%+5d", 1);
checkf ("+1 ", "%+-5d", 1);
checkf (" 1", "% 5d", 1);
checkf ("00001", "%05d", 1);
checkf (" 1", "%5.0d", 1);
checkf (" 01", "%5.2d", 1);
checkf ("1", "%d", 1);
checkf (" -1", "%5d", -1);
checkf ("-1 ", "%-5d", -1);
checkf (" -1", "%+5d", -1);
checkf ("-1 ", "%+-5d", -1);
checkf (" -1", "% 5d", -1);
checkf ("-0001", "%05d", -1);
checkf (" -1", "%5.0d", -1);
checkf (" -01", "%5.2d", -1);
checkf ("-1", "%d", -1);
checkf ("12345", "%5d", 12345);
checkf ("12345", "%-5d", 12345);
checkf ("+12345", "%+5d", 12345);
checkf ("+12345", "%+-5d", 12345);
checkf (" 12345", "% 5d", 12345);
checkf ("12345", "%05d", 12345);
checkf ("12345", "%5.0d", 12345);
checkf ("12345", "%5.2d", 12345);
checkf ("12345", "%d", 12345);
checkf ("123456", "%5d", 123456);
checkf ("123456", "%-5d", 123456);
checkf ("+123456", "%+5d", 123456);
checkf ("+123456", "%+-5d", 123456);
checkf (" 123456", "% 5d", 123456);
checkf ("123456", "%05d", 123456);
checkf ("123456", "%5.0d", 123456);
checkf ("123456", "%5.2d", 123456);
checkf ("123456", "%d", 123456);
/* Check unsigned integer conversions. */
checkf (" 0", "%5u", 0);
checkf (" 0", "%5o", 0);
checkf (" 0", "%5x", 0);
checkf (" 0", "%5X", 0);
checkf (" 0", "%#5o", 0);
checkf (" 0", "%#5x", 0);
checkf (" 0", "%#5X", 0);
checkf (" 00000000", "%#10.8x", 0);
checkf (" 1", "%5u", 1);
checkf (" 1", "%5o", 1);
checkf (" 1", "%5x", 1);
checkf (" 1", "%5X", 1);
checkf (" 01", "%#5o", 1);
checkf (" 0x1", "%#5x", 1);
checkf (" 0X1", "%#5X", 1);
checkf ("0x00000001", "%#10.8x", 1);
checkf ("123456", "%5u", 123456);
checkf ("361100", "%5o", 123456);
checkf ("1e240", "%5x", 123456);
checkf ("1E240", "%5X", 123456);
checkf ("0361100", "%#5o", 123456);
checkf ("0x1e240", "%#5x", 123456);
checkf ("0X1E240", "%#5X", 123456);
checkf ("0x0001e240", "%#10.8x", 123456);
/* Character and string conversions. */
checkf ("foobar", "%c%c%c%c%c%c", 'f', 'o', 'o', 'b', 'a', 'r');
checkf (" left-right ", "%6s%s%-7s", "left", "-", "right");
checkf ("trim", "%.4s", "trimoff");
checkf ("%%", "%%%%");
/* From Cristian Cadar's automatic test case generator. */
checkf (" abcdefgh", "%9s", "abcdefgh");
checkf ("36657730000", "%- o", (unsigned) 036657730000);
checkf ("4139757568", "%- u", (unsigned) 4139757568UL);
checkf ("f6bfb000", "%- x", (unsigned) 0xf6bfb000);
checkf ("36657730000", "%-to", (ptrdiff_t) 036657730000);
checkf ("4139757568", "%-tu", (ptrdiff_t) 4139757568UL);
checkf ("-155209728", "%-zi", (size_t) -155209728);
checkf ("-155209728", "%-zd", (size_t) -155209728);
checkf ("036657730000", "%+#o", (unsigned) 036657730000);
checkf ("0xf6bfb000", "%+#x", (unsigned) 0xf6bfb000);
checkf ("-155209728", "% zi", (size_t) -155209728);
checkf ("-155209728", "% zd", (size_t) -155209728);
checkf ("4139757568", "% tu", (ptrdiff_t) 4139757568UL);
checkf ("036657730000", "% #o", (unsigned) 036657730000);
checkf ("0xf6bfb000", "% #x", (unsigned) 0xf6bfb000);
checkf ("0xf6bfb000", "%# x", (unsigned) 0xf6bfb000);
checkf ("-155209728", "%#zd", (size_t) -155209728);
checkf ("-155209728", "%0zi", (size_t) -155209728);
checkf ("4,139,757,568", "%'tu", (ptrdiff_t) 4139757568UL);
checkf ("-155,209,728", "%-'d", -155209728);
checkf ("-155209728", "%.zi", (size_t) -155209728);
checkf ("-155209728", "%zi", (size_t) -155209728);
checkf ("-155209728", "%zd", (size_t) -155209728);
checkf ("-155209728", "%+zi", (size_t) -155209728);
if (failure_cnt == 0)
printf ("\nstdio: PASS\n");
else
printf ("\nstdio: FAIL: %d tests failed\n", failure_cnt);
}

114
src/tests/internal/stdlib.c Normal file
View File

@@ -0,0 +1,114 @@
/* Test program for sorting and searching in lib/stdlib.c.
Attempts to test the sorting and searching functionality that
is not sufficiently tested elsewhere in PintOS.
This is not a test we will run on your submitted tasks.
It is here for completeness.
*/
#undef NDEBUG
#include <debug.h>
#include <limits.h>
#include <random.h>
#include <stdlib.h>
#include <stdio.h>
#include "threads/test.h"
/* Maximum number of elements in an array that we will test. */
#define MAX_CNT 4096
static void shuffle (int[], size_t);
static int compare_ints (const void *, const void *);
static void verify_order (const int[], size_t);
static void verify_bsearch (const int[], size_t);
/* Test sorting and searching implementations. */
void
test (void)
{
int cnt;
printf ("testing various size arrays:");
for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1)
{
int repeat;
printf (" %zu", cnt);
for (repeat = 0; repeat < 10; repeat++)
{
static int values[MAX_CNT];
int i;
/* Put values 0...CNT in random order in VALUES. */
for (i = 0; i < cnt; i++)
values[i] = i;
shuffle (values, cnt);
/* Sort VALUES, then verify ordering. */
qsort (values, cnt, sizeof *values, compare_ints);
verify_order (values, cnt);
verify_bsearch (values, cnt);
}
}
printf (" done\n");
printf ("stdlib: PASS\n");
}
/* Shuffles the CNT elements in ARRAY into random order. */
static void
shuffle (int *array, size_t cnt)
{
size_t i;
for (i = 0; i < cnt; i++)
{
size_t j = i + random_ulong () % (cnt - i);
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
/* Returns 1 if *A is greater than *B,
0 if *A equals *B,
-1 if *A is less than *B. */
static int
compare_ints (const void *a_, const void *b_)
{
const int *a = a_;
const int *b = b_;
return *a < *b ? -1 : *a > *b;
}
/* Verifies that ARRAY contains the CNT ints 0...CNT-1. */
static void
verify_order (const int *array, size_t cnt)
{
int i;
for (i = 0; (size_t) i < cnt; i++)
ASSERT (array[i] == i);
}
/* Checks that bsearch() works properly in ARRAY. ARRAY must
contain the values 0...CNT-1. */
static void
verify_bsearch (const int *array, size_t cnt)
{
int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2};
int i;
/* Check that all the values in the array are found properly. */
for (i = 0; (size_t) i < cnt; i++)
ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints)
== array + i);
/* Check that some values not in the array are not found. */
not_in_array[0] = cnt;
for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++)
ASSERT (bsearch (&not_in_array[i], array, cnt, sizeof *array, compare_ints)
== NULL);
}

196
src/tests/lib.c Normal file
View File

@@ -0,0 +1,196 @@
#include "tests/lib.h"
#include <random.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <syscall.h>
const char *test_name;
bool quiet = false;
static void
vmsg (const char *format, va_list args, const char *suffix)
{
/* We go to some trouble to stuff the entire message into a
single buffer and output it in a single system call, because
that'll (typically) ensure that it gets sent to the console
atomically. Otherwise kernel messages like "foo: exit(0)"
can end up being interleaved if we're unlucky. */
static char buf[1024];
snprintf (buf, sizeof buf, "(%s) ", test_name);
vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args);
strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf));
write (STDOUT_FILENO, buf, strlen (buf));
}
void
msg (const char *format, ...)
{
va_list args;
if (quiet)
return;
va_start (args, format);
vmsg (format, args, "\n");
va_end (args);
}
void
fail (const char *format, ...)
{
va_list args;
va_start (args, format);
vmsg (format, args, ": FAILED\n");
va_end (args);
exit (1);
}
static void
swap (void *a_, void *b_, size_t size)
{
uint8_t *a = a_;
uint8_t *b = b_;
size_t i;
for (i = 0; i < size; i++)
{
uint8_t t = a[i];
a[i] = b[i];
b[i] = t;
}
}
void
shuffle (void *buf_, size_t cnt, size_t size)
{
char *buf = buf_;
size_t i;
for (i = 0; i < cnt; i++)
{
size_t j = i + random_ulong () % (cnt - i);
swap (buf + i * size, buf + j * size, size);
}
}
void
exec_children (const char *child_name, pid_t pids[], size_t child_cnt)
{
size_t i;
for (i = 0; i < child_cnt; i++)
{
char cmd_line[128];
snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i);
CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR,
"exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line);
}
}
void
wait_children (pid_t pids[], size_t child_cnt)
{
size_t i;
for (i = 0; i < child_cnt; i++)
{
int status = wait (pids[i]);
CHECK (status == (int) i,
"wait for child %zu of %zu returned %d (expected %zu)",
i + 1, child_cnt, status, i);
}
}
void
check_file_handle (int fd,
const char *file_name, const void *buf_, size_t size)
{
const char *buf = buf_;
size_t ofs = 0;
size_t file_size;
/* Warn about file of wrong size. Don't fail yet because we
may still be able to get more information by reading the
file. */
file_size = (size_t) filesize (fd);
if (file_size != size)
msg ("size of %s (%zu) differs from expected (%zu)",
file_name, file_size, size);
/* Read the file block-by-block, comparing data as we go. */
while (ofs < size)
{
char block[512];
size_t block_size, ret_val;
block_size = size - ofs;
if (block_size > sizeof block)
block_size = sizeof block;
ret_val = (size_t) read (fd, block, block_size);
if (ret_val != block_size)
fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu",
block_size, ofs, file_name, ret_val);
compare_bytes (block, buf + ofs, block_size, ofs, file_name);
ofs += block_size;
}
/* Now fail due to wrong file size. */
if (file_size != size)
fail ("size of %s (%zu) differs from expected (%zu)",
file_name, file_size, size);
msg ("verified contents of \"%s\"", file_name);
}
void
check_file (const char *file_name, const void *buf, size_t size)
{
int fd;
CHECK ((fd = open (file_name)) > 1, "open \"%s\" for verification",
file_name);
check_file_handle (fd, file_name, buf, size);
msg ("close \"%s\"", file_name);
close (fd);
}
void
compare_bytes (const void *read_data_, const void *expected_data_, size_t size,
size_t ofs, const char *file_name)
{
const uint8_t *read_data = read_data_;
const uint8_t *expected_data = expected_data_;
size_t i, j;
size_t show_cnt;
if (!memcmp (read_data, expected_data, size))
return;
for (i = 0; i < size; i++)
if (read_data[i] != expected_data[i])
break;
for (j = i + 1; j < size; j++)
if (read_data[j] == expected_data[j])
break;
quiet = false;
msg ("%zu bytes read starting at offset %zu in \"%s\" differ "
"from expected.", j - i, ofs + i, file_name);
show_cnt = j - i;
if (j - i > 64)
{
show_cnt = 64;
msg ("Showing first differing %zu bytes.", show_cnt);
}
msg ("Data actually read:");
hex_dump (ofs + i, read_data + i, show_cnt, true);
msg ("Expected data:");
hex_dump (ofs + i, expected_data + i, show_cnt, true);
fail ("%zu bytes read starting at offset %zu in \"%s\" differ "
"from expected", j - i, ofs + i, file_name);
}

50
src/tests/lib.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef TESTS_LIB_H
#define TESTS_LIB_H
#include <debug.h>
#include <stdbool.h>
#include <stddef.h>
#include <syscall.h>
extern const char *test_name;
extern bool quiet;
void msg (const char *, ...) PRINTF_FORMAT (1, 2);
void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN;
/* Takes an expression to test for SUCCESS and a message, which
may include printf-style arguments. Logs the message, then
tests the expression. If it is zero, indicating failure,
emits the message as a failure.
Somewhat tricky to use:
- SUCCESS must not have side effects that affect the
message, because that will cause the original message and
the failure message to differ.
- The message must not have side effects of its own, because
it will be printed twice on failure, or zero times on
success if quiet is set. */
#define CHECK(SUCCESS, ...) \
do \
{ \
msg (__VA_ARGS__); \
if (!(SUCCESS)) \
fail (__VA_ARGS__); \
} \
while (0)
void shuffle (void *, size_t cnt, size_t size);
void exec_children (const char *child_name, pid_t pids[], size_t child_cnt);
void wait_children (pid_t pids[], size_t child_cnt);
void check_file_handle (int fd, const char *file_name,
const void *buf_, size_t filesize);
void check_file (const char *file_name, const void *buf, size_t filesize);
void compare_bytes (const void *read_data, const void *expected_data,
size_t size, size_t ofs, const char *file_name);
#endif /* test/lib.h */

19
src/tests/lib.pm Normal file
View File

@@ -0,0 +1,19 @@
use strict;
use warnings;
use tests::random;
sub shuffle {
my ($in, $cnt, $sz) = @_;
$cnt * $sz == length $in or die;
my (@a) = 0...$cnt - 1;
for my $i (0...$cnt - 1) {
my ($j) = $i + random_ulong () % ($cnt - $i);
@a[$i, $j] = @a[$j, $i];
}
my ($out) = "";
$out .= substr ($in, $_ * $sz, $sz) foreach @a;
return $out;
}
1;

15
src/tests/main.c Normal file
View File

@@ -0,0 +1,15 @@
#include <random.h>
#include "tests/lib.h"
#include "tests/main.h"
int
main (int argc UNUSED, char *argv[])
{
test_name = argv[0];
msg ("begin");
random_init (0);
test_main ();
msg ("end");
return 0;
}

6
src/tests/main.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef TESTS_MAIN_H
#define TESTS_MAIN_H
void test_main (void);
#endif /* tests/main.h */

152
src/tests/make-grade Executable file
View File

@@ -0,0 +1,152 @@
#! /usr/bin/perl
use strict;
use warnings;
@ARGV == 3 || die;
my ($src_dir, $results_file, $grading_file) = @ARGV;
# Read pass/file verdicts from $results_file.
open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n";
my (%verdicts, %verdict_counts);
while (<RESULTS>) {
my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die;
$verdicts{$test} = $verdict eq 'pass';
}
close RESULTS;
my (@failures);
my (@overall, @rubrics, @summary);
my ($pct_actual, $pct_possible) = (0, 0);
# Read grading file.
my (@items);
open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n";
while (<GRADING>) {
s/#.*//;
next if /^\s*$/;
my ($max_pct, $rubric_suffix) = /^\s*(\d+(?:\.\d+)?)%\t(.*)/ or die;
my ($dir) = $rubric_suffix =~ /^(.*)\//;
my ($rubric_file) = "$src_dir/$rubric_suffix";
open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n";
# Rubric file must begin with title line.
my $title = <RUBRIC>;
chomp $title;
$title =~ s/:$// or die;
$title .= " ($rubric_suffix):";
push (@rubrics, $title);
my ($score, $possible) = (0, 0);
my ($cnt, $passed) = (0, 0);
my ($was_score) = 0;
while (<RUBRIC>) {
chomp;
push (@rubrics, "\t$_"), next if /^-/;
push (@rubrics, ""), next if /^\s*$/;
my ($poss, $name) = /^(\d+)\t(.*)$/ or die;
my ($test) = "$dir/$name";
my ($points) = 0;
if (!defined $verdicts{$test}) {
push (@overall, "warning: $test not tested, assuming failure");
} elsif ($verdicts{$test}) {
$points = $poss;
$passed++;
}
push (@failures, $test) if !$points;
$verdict_counts{$test}++;
push (@rubrics, sprintf ("\t%4s%2d/%2d %s",
$points ? '' : '**', $points, $poss, $test));
$score += $points;
$possible += $poss;
$cnt++;
}
close (RUBRIC);
push (@rubrics, "");
push (@rubrics, "\t- Section summary.");
push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
'', $passed, $cnt, 'tests passed'));
push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
'', $score, $possible, 'points subtotal'));
push (@rubrics, '');
my ($pct) = ($score / $possible) * $max_pct;
push (@summary, sprintf ("%-45s %3d/%3d %5.1f%%/%5.1f%%",
$rubric_suffix,
$score, $possible,
$pct, $max_pct));
$pct_actual += $pct;
$pct_possible += $max_pct;
}
close GRADING;
my ($sum_line)
= "--------------------------------------------- --- --- ------ ------";
unshift (@summary,
"SUMMARY BY TEST SET",
'',
sprintf ("%-45s %3s %3s %6s %6s",
"Test Set", "Pts", "Max", "% Ttl", "% Max"),
$sum_line);
push (@summary,
$sum_line,
sprintf ("%-45s %3s %3s %5.1f%%/%5.1f%%",
'Total', '', '', $pct_actual, $pct_possible));
unshift (@rubrics,
"SUMMARY OF INDIVIDUAL TESTS",
'');
foreach my $name (keys (%verdicts)) {
my ($count) = $verdict_counts{$name};
if (!defined ($count) || $count != 1) {
if (!defined ($count) || !$count) {
push (@overall, "warning: test $name doesn't count for grading");
} else {
push (@overall,
"warning: test $name counted $count times in grading");
}
}
}
push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual));
if (sprintf ("%.1f", $pct_actual) eq sprintf ("%.1f", $pct_possible)) {
push (@overall, "ALL TESTED PASSED -- PERFECT SCORE");
}
my (@divider) = ('', '- ' x 38, '');
print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics);
for my $test (@failures) {
print map ("$_\n", @divider);
print "DETAILS OF $test FAILURE:\n\n";
if (open (RESULT, '<', "$test.result")) {
my $first_line = <RESULT>;
my ($cnt) = 0;
while (<RESULT>) {
print;
$cnt++;
}
close (RESULT);
}
if (open (OUTPUT, '<', "$test.output")) {
print "\nOUTPUT FROM $test:\n\n";
my ($panics, $boots) = (0, 0);
while (<OUTPUT>) {
if (/PANIC/ && ++$panics > 2) {
print "[...details of additional panic(s) omitted...]\n";
last;
}
print;
if (/PintOS booting/ && ++$boots > 1) {
print "[...details of reboot(s) omitted...]\n";
last;
}
}
close (OUTPUT);
}
}

27
src/tests/random.pm Normal file
View File

@@ -0,0 +1,27 @@
use strict;
use warnings;
use tests::arc4;
my (@arc4);
sub random_init {
if (@arc4 == 0) {
my ($seed) = @_;
$seed = 0 if !defined $seed;
@arc4 = arc4_init (pack ("V", $seed));
}
}
sub random_bytes {
random_init ();
my ($n) = @_;
return arc4_crypt (\@arc4, "\0" x $n);
}
sub random_ulong {
random_init ();
return unpack ("V", random_bytes (4));
}
1;

650
src/tests/tests.pm Normal file
View File

@@ -0,0 +1,650 @@
use strict;
use warnings;
use tests::Algorithm::Diff;
use File::Temp 'tempfile';
use Fcntl qw(SEEK_SET SEEK_CUR);
sub fail;
sub pass;
die if @ARGV != 2;
our ($test, $src_dir) = @ARGV;
my ($msg_file) = tempfile ();
select ($msg_file);
our (@prereq_tests) = ();
if ($test =~ /^(.*)-persistence$/) {
push (@prereq_tests, $1);
}
for my $prereq_test (@prereq_tests) {
my (@result) = read_text_file ("$prereq_test.result");
fail "Prerequisite test $prereq_test failed.\n" if $result[0] ne 'PASS';
}
# Generic testing.
sub check_expected {
my ($expected) = pop @_;
my (@options) = @_;
my (@output) = read_text_file ("$test.output");
common_checks ("run", @output);
compare_output ("run", @options, \@output, $expected);
}
sub common_checks {
my ($run, @output) = @_;
fail "\u$run produced no output at all\n" if @output == 0;
check_for_panic ($run, @output);
check_for_keyword ($run, "FAIL", @output);
check_for_triple_fault ($run, @output);
check_for_keyword ($run, "TIMEOUT", @output);
fail "\u$run didn't start up properly: no \"PintOS booting\" message\n"
if !grep (/PintOS booting with.*kB RAM\.\.\./, @output);
fail "\u$run didn't start up properly: no \"Boot complete\" message\n"
if !grep (/Boot complete/, @output);
fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n"
if !grep (/Timer: \d+ ticks/, @output);
fail "\u$run didn't shut down properly: no \"Powering off\" message\n"
if !grep (/Powering off/, @output);
}
sub check_for_panic {
my ($run, @output) = @_;
my ($panic) = grep (/PANIC/, @output);
return unless defined $panic;
print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")),
"\n";
my (@stack_line) = grep (/Call stack:/, @output);
if (@stack_line != 0) {
my ($addrs) = $stack_line[0] =~ /Call stack:((?: 0x[0-9a-f]+)+)/;
# Find a user program to translate user virtual addresses.
my ($userprog) = "";
$userprog = "$test"
if grep (hex ($_) < 0xc0000000, split (' ', $addrs)) > 0 && -e $test;
# Get and print the backtrace.
my ($trace) = scalar (`backtrace kernel.o $userprog $addrs`);
print "Call stack:$addrs\n";
print "Translation of call stack:\n";
print $trace;
# Print disclaimer.
if ($userprog ne '' && index ($trace, $userprog) >= 0) {
print <<EOF;
Translations of user virtual addresses above are based on a guess at
the binary to use. If this guess is incorrect, then those
translations will be misleading.
EOF
}
}
if ($panic =~ /sec_no \< d-\>capacity/) {
print <<EOF;
\nThis assertion commonly fails when accessing a file via an inode that
has been closed and freed. Freeing an inode clears all its sector
indexes to 0xcccccccc, which is not a valid sector number for disks
smaller than about 1.6 TB.
EOF
}
fail;
}
sub check_for_keyword {
my ($run, $keyword, @output) = @_;
my ($kw_line) = grep (/$keyword/, @output);
return unless defined $kw_line;
# Most output lines are prefixed by (test-name). Eliminate this
# from our message for brevity.
$kw_line =~ s/^\([^\)]+\)\s+//;
print "$run: $kw_line\n";
fail;
}
sub check_for_triple_fault {
my ($run, @output) = @_;
my ($reboots) = grep (/PintOS booting/, @output) - 1;
return unless $reboots > 0;
print <<EOF;
\u$run spontaneously rebooted $reboots times.
This is most often caused by unhandled page faults.
Read the Triple Faults section in the Debugging chapter
of the PintOS manual for more information.
EOF
fail;
}
# Get @output without header or trailer.
sub get_core_output {
my ($run, @output) = @_;
my ($p);
my ($process);
my ($start);
for my $i (0...$#_) {
$start = $i + 1, last
if ($process) = $output[$i] =~ /^Executing '(\S+).*':$/;
}
my ($end);
for my $i ($start...$#output) {
$end = $i - 1, last if $output[$i] =~ /^Execution of '.*' complete.$/;
}
fail "\u$run didn't start a thread or process\n" if !defined $start;
fail "\u$run started '$process' but it never finished\n" if !defined $end;
return @output[$start...$end];
}
sub compare_output {
my ($run) = shift @_;
my ($expected) = pop @_;
my ($output) = pop @_;
my (%options) = @_;
my (@output) = get_core_output ($run, @$output);
fail "\u$run didn't produce any output" if !@output;
my $ignore_exit_codes = exists $options{IGNORE_EXIT_CODES};
if ($ignore_exit_codes) {
delete $options{IGNORE_EXIT_CODES};
@output = grep (!/^[a-zA-Z0-9-_]+: exit\(\-?\d+\)$/, @output);
}
my $ignore_user_faults = exists $options{IGNORE_USER_FAULTS};
if ($ignore_user_faults) {
delete $options{IGNORE_USER_FAULTS};
@output = grep (!/^Page fault at.*in user context\.$/
&& !/: dying due to interrupt 0x0e \(.*\).$/
&& !/^Interrupt 0x0e \(.*\) at eip=/
&& !/^ cr2=.* error=.*/
&& !/^ eax=.* ebx=.* ecx=.* edx=.*/
&& !/^ esi=.* edi=.* esp=.* ebp=.*/
&& !/^ cs=.* ds=.* es=.* ss=.*/, @output);
}
my $ignore_kernel_faults = exists $options{IGNORE_KERNEL_FAULTS};
if ($ignore_kernel_faults) {
delete $options{IGNORE_KERNEL_FAULTS};
@output = grep (!/^Page fault at.*in kernel context\.$/
&& !/: dying due to interrupt 0x0e \(.*\).$/
&& !/^Interrupt 0x0e \(.*\) at eip=/
&& !/^ cr2=.* error=.*/
&& !/^ eax=.* ebx=.* ecx=.* edx=.*/
&& !/^ esi=.* edi=.* esp=.* ebp=.*/
&& !/^ cs=.* ds=.* es=.* ss=.*/, @output);
}
my $ignore_div0_faults = exists $options{IGNORE_DIV0_FAULTS};
if ($ignore_div0_faults) {
delete $options{IGNORE_DIV0_FAULTS};
@output = grep (!/: dying due to interrupt 0000 \(.*\).$/
&& !/^Interrupt 0000 \(.*\) at eip=/
&& !/^ cr2=.* error=.*/
&& !/^ eax=.* ebx=.* ecx=.* edx=.*/
&& !/^ esi=.* edi=.* esp=.* ebp=.*/
&& !/^ cs=.* ds=.* es=.* ss=.*/, @output);
}
die "unknown option " . (keys (%options))[0] . "\n" if %options;
my ($msg);
# Compare actual output against each allowed output.
if (ref ($expected) eq 'ARRAY') {
my ($i) = 0;
$expected = {map ((++$i => $_), @$expected)};
}
foreach my $key (keys %$expected) {
my (@expected) = split ("\n", $expected->{$key});
$msg .= "Acceptable output:\n";
$msg .= join ('', map (" $_\n", @expected));
# Check whether actual and expected match.
# If it's a perfect match, we're done.
if ($#output == $#expected) {
my ($eq) = 1;
for (my ($i) = 0; $i <= $#expected; $i++) {
$eq = 0 if $output[$i] ne $expected[$i];
}
return $key if $eq;
}
# They differ. Output a diff.
my (@diff) = "";
my ($d) = Algorithm::Diff->new (\@expected, \@output);
while ($d->Next ()) {
my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2));
if ($d->Same ()) {
push (@diff, map (" $_\n", $d->Items (1)));
} else {
push (@diff, map ("- $_\n", $d->Items (1))) if $d->Items (1);
push (@diff, map ("+ $_\n", $d->Items (2))) if $d->Items (2);
}
}
$msg .= "Differences in `diff -u' format:\n";
$msg .= join ('', @diff);
}
# Failed to match. Report failure.
$msg .= "\n(Process exit codes are excluded for matching purposes.)\n"
if $ignore_exit_codes;
$msg .= "\n(User fault messages are excluded for matching purposes.)\n"
if $ignore_user_faults;
$msg .= "\n(Kernel fault messages are excluded for matching purposes.)\n"
if $ignore_kernel_faults;
$msg .= "\n(Divide Error messages are excluded for matching purposes.)\n"
if $ignore_div0_faults;
fail "Test output failed to match any acceptable form.\n\n$msg";
}
# File system extraction.
# check_archive (\%CONTENTS)
#
# Checks that the extracted file system's contents match \%CONTENTS.
# Each key in the hash is a file name. Each value may be:
#
# - $FILE: Name of a host file containing the expected contents.
#
# - [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE
# comprising the $LENGTH bytes starting at $OFFSET.
#
# - [$CONTENTS]: The literal expected file contents, as a string.
#
# - {SUBDIR}: A subdirectory, in the same form described here,
# recursively.
sub check_archive {
my ($expected_hier) = @_;
my (@output) = read_text_file ("$test.output");
common_checks ("file system extraction run", @output);
@output = get_core_output ("file system extraction run", @output);
@output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output);
fail join ("\n", "Error extracting file system:", @output) if @output;
my ($test_base_name) = $test;
$test_base_name =~ s%.*/%%;
$test_base_name =~ s%-persistence$%%;
$expected_hier->{$test_base_name} = $prereq_tests[0];
$expected_hier->{'tar'} = 'tests/filesys/extended/tar';
my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, ""));
my (%actual) = read_tar ("$prereq_tests[0].tar");
my ($errors) = 0;
foreach my $name (sort keys %expected) {
if (exists $actual{$name}) {
if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) {
print "$name is a directory but should be an ordinary file.\n";
$errors++;
} elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) {
print "$name is an ordinary file but should be a directory.\n";
$errors++;
}
} else {
print "$name is missing from the file system.\n";
$errors++;
}
}
foreach my $name (sort keys %actual) {
if (!exists $expected{$name}) {
if ($name =~ /^[[:print:]]+$/) {
print "$name exists in the file system but it should not.\n";
} else {
my ($esc_name) = $name;
$esc_name =~ s/[^[:print:]]/./g;
print <<EOF;
$esc_name exists in the file system but should not. (The name
of this file contains unusual characters that were printed as `.'.)
EOF
}
$errors++;
}
}
if ($errors) {
print "\nActual contents of file system:\n";
print_fs (%actual);
print "\nExpected contents of file system:\n";
print_fs (%expected);
} else {
foreach my $name (sort keys %expected) {
if (!is_dir ($expected{$name})) {
my ($exp_file, $exp_length) = open_file ($expected{$name});
my ($act_file, $act_length) = open_file ($actual{$name});
$errors += !compare_files ($exp_file, $exp_length,
$act_file, $act_length, $name,
!$errors);
close ($exp_file);
close ($act_file);
}
}
}
fail "Extracted file system contents are not correct.\n" if $errors;
}
# open_file ([$FILE, $OFFSET, $LENGTH])
# open_file ([$CONTENTS])
#
# Opens a file for the contents passed in, which must be in one of
# the two above forms that correspond to check_archive() arguments.
#
# Returns ($HANDLE, $LENGTH), where $HANDLE is the file's handle and
# $LENGTH is the number of bytes in the file's content.
sub open_file {
my ($value) = @_;
die if ref ($value) ne 'ARRAY';
my ($file) = tempfile ();
my ($length);
if (@$value == 1) {
$length = length ($value->[0]);
$file = tempfile ();
syswrite ($file, $value->[0]) == $length
or die "writing temporary file: $!\n";
sysseek ($file, 0, SEEK_SET);
} elsif (@$value == 3) {
$length = $value->[2];
open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n";
die "$value->[0]: file is smaller than expected\n"
if -s $file < $value->[1] + $length;
sysseek ($file, $value->[1], SEEK_SET);
} else {
die;
}
return ($file, $length);
}
# compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE)
#
# Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B.
# ($A and $B are handles.)
# If their contents differ, prints a brief message describing
# the differences, using $NAME to identify the file.
# The message contains more detail if $VERBOSE is nonzero.
# Returns 1 if the contents are identical, 0 otherwise.
sub compare_files {
my ($a, $a_size, $b, $b_size, $name, $verbose) = @_;
my ($ofs) = 0;
select(STDOUT);
for (;;) {
my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size;
my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size;
my ($a_data, $b_data);
if (!defined (sysread ($a, $a_data, $a_amt))
|| !defined (sysread ($b, $b_data, $b_amt))) {
die "reading $name: $!\n";
}
my ($a_len) = length $a_data;
my ($b_len) = length $b_data;
last if $a_len == 0 && $b_len == 0;
if ($a_data ne $b_data) {
my ($min_len) = $a_len < $b_len ? $a_len : $b_len;
my ($diff_ofs);
for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) {
last if (substr ($a_data, $diff_ofs, 1)
ne substr ($b_data, $diff_ofs, 1));
}
printf "\nFile $name differs from expected "
. "starting at offset 0x%x.\n", $ofs + $diff_ofs;
if ($verbose ) {
print "Expected contents:\n";
hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs);
print "Actual contents:\n";
hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs);
}
return 0;
}
$ofs += $a_len;
$a_size -= $a_len;
$b_size -= $b_len;
}
return 1;
}
# hex_dump ($DATA, $OFS)
#
# Prints $DATA in hex and text formats.
# The first byte of $DATA corresponds to logical offset $OFS
# in whatever file the data comes from.
sub hex_dump {
my ($data, $ofs) = @_;
if ($data eq '') {
printf " (File ends at offset %08x.)\n", $ofs;
return;
}
my ($per_line) = 16;
while ((my $size = length ($data)) > 0) {
my ($start) = $ofs % $per_line;
my ($end) = $per_line;
$end = $start + $size if $end - $start > $size;
my ($n) = $end - $start;
printf "0x%08x ", int ($ofs / $per_line) * $per_line;
# Hex version.
print " " x $start;
for my $i ($start...$end - 1) {
printf "%02x", ord (substr ($data, $i - $start, 1));
print $i == $per_line / 2 - 1 ? '-' : ' ';
}
print " " x ($per_line - $end);
# Character version.
my ($esc_data) = substr ($data, 0, $n);
$esc_data =~ s/[^[:print:]]/./g;
print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|";
print "\n";
$data = substr ($data, $n);
$ofs += $n;
}
}
# print_fs (%FS)
#
# Prints a list of files in %FS, which must be a file system
# as flattened by flatten_hierarchy() and normalized by
# normalize_fs().
sub print_fs {
my (%fs) = @_;
foreach my $name (sort keys %fs) {
my ($esc_name) = $name;
$esc_name =~ s/[^[:print:]]/./g;
print "$esc_name: ";
if (!is_dir ($fs{$name})) {
print +file_size ($fs{$name}), "-byte file";
} else {
print "directory";
}
print "\n";
}
print "(empty)\n" if !@_;
}
# normalize_fs (%FS)
#
# Takes a file system as flattened by flatten_hierarchy().
# Returns a similar file system in which values of the form $FILE
# are replaced by those of the form [$FILE, $OFFSET, $LENGTH].
sub normalize_fs {
my (%fs) = @_;
foreach my $name (keys %fs) {
my ($value) = $fs{$name};
next if is_dir ($value) || ref ($value) ne '';
die "can't open $value\n" if !stat $value;
$fs{$name} = [$value, 0, -s _];
}
return %fs;
}
# is_dir ($VALUE)
#
# Takes a value like one in the hash returned by flatten_hierarchy()
# and returns 1 if it represents a directory, 0 otherwise.
sub is_dir {
my ($value) = @_;
return ref ($value) eq '' && $value eq 'directory';
}
# file_size ($VALUE)
#
# Takes a value like one in the hash returned by flatten_hierarchy()
# and returns the size of the file it represents.
sub file_size {
my ($value) = @_;
die if is_dir ($value);
die if ref ($value) ne 'ARRAY';
return @$value > 1 ? $value->[2] : length ($value->[0]);
}
# flatten_hierarchy ($HIER_FS, $PREFIX)
#
# Takes a file system in the format expected by check_archive() and
# returns a "flattened" version in which file names include all parent
# directory names and the value of directories is just "directory".
sub flatten_hierarchy {
my (%hier_fs) = %{$_[0]};
my ($prefix) = $_[1];
my (%flat_fs);
for my $name (keys %hier_fs) {
my ($value) = $hier_fs{$name};
if (ref $value eq 'HASH') {
%flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/"));
$flat_fs{"$prefix$name"} = 'directory';
} else {
$flat_fs{"$prefix$name"} = $value;
}
}
return %flat_fs;
}
# read_tar ($ARCHIVE)
#
# Reads the ustar-format tar file in $ARCHIVE
# and returns a flattened file system for it.
sub read_tar {
my ($archive) = @_;
my (%content);
open (ARCHIVE, '<', $archive) or fail "$archive: open: $!\n";
for (;;) {
my ($header);
if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) {
fail "$archive: unexpected end of file\n" if $retval >= 0;
fail "$archive: read: $!\n";
}
last if $header eq "\0" x 512;
# Verify magic numbers.
if (substr ($header, 257, 6) ne "ustar\0"
|| substr ($header, 263, 2) ne '00') {
fail "$archive: corrupt ustar header\n";
}
# Verify checksum.
my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8)));
my ($correct_chksum) = unpack ("%32a*", $header);
fail "$archive: bad header checksum\n" if $chksum != $correct_chksum;
# Get file name.
my ($name) = unpack ("Z100", $header);
my ($prefix) = unpack ("Z*", substr ($header, 345));
$name = "$prefix/$name" if $prefix ne '';
fail "$archive: contains file with empty name" if $name eq '';
# Get type.
my ($typeflag) = substr ($header, 156, 1);
$typeflag = '0' if $typeflag eq "\0";
fail "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/;
# Get size.
my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
fail "bad size $size\n" if $size < 0;
$size = 0 if $typeflag eq '5';
# Store content.
$name =~ s%^(/|\./|\.\./)*%%; # Strip leading "/", "./", "../".
$name = '' if $name eq '.' || $name eq '..';
if (exists $content{$name}) {
fail "$archive: contains multiple entries for $name\n";
}
if ($typeflag eq '5') {
$content{$name} = 'directory' if $name ne '';
} else {
fail "$archive: contains file with empty name\n" if $name eq '';
my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR);
$content{$name} = [$archive, $position, $size];
sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR);
}
}
close (ARCHIVE);
return %content;
}
# Utilities.
sub fail {
finish ("FAIL", @_);
}
sub pass {
finish ("PASS", @_);
}
sub finish {
my ($verdict, @messages) = @_;
seek ($msg_file, 0, 0);
push (@messages, <$msg_file>);
close ($msg_file);
chomp (@messages);
my ($result_fn) = "$test.result";
open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n";
print RESULT "$verdict\n";
print RESULT "$_\n" foreach @messages;
close (RESULT);
if ($verdict eq 'PASS') {
print STDOUT "pass $test\n";
} else {
print STDOUT "FAIL $test\n";
}
print STDOUT "$_\n" foreach @messages;
exit 0;
}
sub read_text_file {
my ($file_name) = @_;
open (FILE, '<', $file_name) or die "$file_name: open: $!\n";
my (@content) = <FILE>;
chomp (@content);
close (FILE);
return @content;
}
1;

View File

@@ -0,0 +1,7 @@
# Percentage of the testing point total designated for each set of tests.
5.0% tests/devices/Rubric.alarmfunc
5.0% tests/devices/Rubric.alarmrobust
45.0% tests/threads/Rubric.priority
0.0% tests/threads/Rubric.priorityCR
45.0% tests/threads/Rubric.mlfqs

View File

@@ -0,0 +1,49 @@
# -*- makefile -*-
# Test names.
tests/threads_TESTS = $(addprefix tests/threads/, \
alarm-priority priority-change priority-donate-one \
priority-donate-multiple priority-donate-multiple2 \
priority-donate-nest priority-donate-sema priority-donate-lower \
priority-fifo priority-preempt priority-sema priority-condvar \
priority-donate-chain priority-preservation \
mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 mlfqs-fair-2 \
mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block)
# Sources for tests.
tests/threads_SRC = tests/threads/tests.c
tests/threads_SRC += tests/threads/alarm-priority.c
tests/threads_SRC += tests/threads/priority-change.c
tests/threads_SRC += tests/threads/priority-donate-one.c
tests/threads_SRC += tests/threads/priority-donate-multiple.c
tests/threads_SRC += tests/threads/priority-donate-multiple2.c
tests/threads_SRC += tests/threads/priority-donate-nest.c
tests/threads_SRC += tests/threads/priority-donate-sema.c
tests/threads_SRC += tests/threads/priority-donate-lower.c
tests/threads_SRC += tests/threads/priority-fifo.c
tests/threads_SRC += tests/threads/priority-preempt.c
tests/threads_SRC += tests/threads/priority-sema.c
tests/threads_SRC += tests/threads/priority-condvar.c
tests/threads_SRC += tests/threads/priority-donate-chain.c
tests/threads_SRC += tests/threads/priority-preservation.c
tests/threads_SRC += tests/threads/mlfqs-load-1.c
tests/threads_SRC += tests/threads/mlfqs-load-60.c
tests/threads_SRC += tests/threads/mlfqs-load-avg.c
tests/threads_SRC += tests/threads/mlfqs-recent-1.c
tests/threads_SRC += tests/threads/mlfqs-fair.c
tests/threads_SRC += tests/threads/mlfqs-block.c
MLFQS_OUTPUTS = \
tests/threads/mlfqs-load-1.output \
tests/threads/mlfqs-load-60.output \
tests/threads/mlfqs-load-avg.output \
tests/threads/mlfqs-recent-1.output \
tests/threads/mlfqs-fair-2.output \
tests/threads/mlfqs-fair-20.output \
tests/threads/mlfqs-nice-2.output \
tests/threads/mlfqs-nice-10.output \
tests/threads/mlfqs-block.output
$(MLFQS_OUTPUTS): KERNELFLAGS += -mlfqs
$(MLFQS_OUTPUTS): TIMEOUT = 480

View File

@@ -0,0 +1,14 @@
Functionality of advanced scheduler:
5 mlfqs-load-1
5 mlfqs-load-60
5 mlfqs-load-avg
5 mlfqs-recent-1
5 mlfqs-fair-2
5 mlfqs-fair-20
5 mlfqs-nice-2
5 mlfqs-nice-10
5 mlfqs-block

View File

@@ -0,0 +1,16 @@
Functionality of priority scheduler:
5 alarm-priority
5 priority-change
5 priority-preempt
5 priority-fifo
5 priority-sema
5 priority-condvar
5 priority-donate-one
5 priority-donate-multiple
5 priority-donate-multiple2
5 priority-donate-nest
10 priority-donate-chain
5 priority-donate-sema
5 priority-donate-lower

View File

@@ -0,0 +1,2 @@
Full correctness of priority scheduler:
10 priority-preservation

View File

@@ -0,0 +1,58 @@
/* Checks that when the alarm clock wakes up threads, the
higher-priority threads run first. */
#include <stdio.h>
#include "tests/threads/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
static thread_func alarm_priority_thread;
static int64_t wake_time;
static struct semaphore wait_sema;
void
test_alarm_priority (void)
{
int i;
/* This test does not work with the MLFQS. */
ASSERT (!thread_mlfqs);
wake_time = timer_ticks () + 5 * TIMER_FREQ;
sema_init (&wait_sema, 0);
for (i = 0; i < 10; i++)
{
int priority = PRI_DEFAULT - (i + 5) % 10 - 1;
char name[16];
snprintf (name, sizeof name, "priority %d", priority);
thread_create (name, priority, alarm_priority_thread, NULL);
}
thread_set_priority (PRI_MIN);
for (i = 0; i < 10; i++)
sema_down (&wait_sema);
}
static void
alarm_priority_thread (void *aux UNUSED)
{
/* Busy-wait until the current time changes. */
int64_t start_time = timer_ticks ();
while (timer_elapsed (start_time) == 0)
continue;
/* Now we know we're at the very beginning of a timer tick, so
we can call timer_sleep() without worrying about races
between checking the time and a timer interrupt. */
timer_sleep (wake_time - timer_ticks ());
/* Print a message on wake-up. */
msg ("Thread %s woke up.", thread_name ());
sema_up (&wait_sema);
}

View File

@@ -0,0 +1,19 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected ([<<'EOF']);
(alarm-priority) begin
(alarm-priority) Thread priority 30 woke up.
(alarm-priority) Thread priority 29 woke up.
(alarm-priority) Thread priority 28 woke up.
(alarm-priority) Thread priority 27 woke up.
(alarm-priority) Thread priority 26 woke up.
(alarm-priority) Thread priority 25 woke up.
(alarm-priority) Thread priority 24 woke up.
(alarm-priority) Thread priority 23 woke up.
(alarm-priority) Thread priority 22 woke up.
(alarm-priority) Thread priority 21 woke up.
(alarm-priority) end
EOF
pass;

View File

@@ -0,0 +1,64 @@
/* Checks that recent_cpu and priorities are updated for blocked
threads.
The main thread sleeps for 25 seconds, spins for 5 seconds,
then releases a lock. The "block" thread spins for 20 seconds
then attempts to acquire the lock, which will block for 10
seconds (until the main thread releases it). If recent_cpu
decays properly while the "block" thread sleeps, then the
block thread should be immediately scheduled when the main
thread releases the lock. */
#include <stdio.h>
#include "tests/threads/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
static void block_thread (void *lock_);
void
test_mlfqs_block (void)
{
int64_t start_time;
struct lock lock;
ASSERT (thread_mlfqs);
msg ("Main thread acquiring lock.");
lock_init (&lock);
lock_acquire (&lock);
msg ("Main thread creating block thread, sleeping 25 seconds...");
thread_create ("block", PRI_DEFAULT, block_thread, &lock);
timer_sleep (25 * TIMER_FREQ);
msg ("Main thread spinning for 5 seconds...");
start_time = timer_ticks ();
while (timer_elapsed (start_time) < 5 * TIMER_FREQ)
continue;
msg ("Main thread releasing lock.");
lock_release (&lock);
msg ("Block thread should have already acquired lock.");
}
static void
block_thread (void *lock_)
{
struct lock *lock = lock_;
int64_t start_time;
msg ("Block thread spinning for 20 seconds...");
start_time = timer_ticks ();
while (timer_elapsed (start_time) < 20 * TIMER_FREQ)
continue;
msg ("Block thread acquiring lock...");
lock_acquire (lock);
msg ("...got it.");
}

View File

@@ -0,0 +1,17 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
check_expected ([<<'EOF']);
(mlfqs-block) begin
(mlfqs-block) Main thread acquiring lock.
(mlfqs-block) Main thread creating block thread, sleeping 25 seconds...
(mlfqs-block) Block thread spinning for 20 seconds...
(mlfqs-block) Block thread acquiring lock...
(mlfqs-block) Main thread spinning for 5 seconds...
(mlfqs-block) Main thread releasing lock.
(mlfqs-block) ...got it.
(mlfqs-block) Block thread should have already acquired lock.
(mlfqs-block) end
EOF
pass;

View File

@@ -0,0 +1,7 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
use tests::threads::mlfqs;
check_mlfqs_fair ([0, 0], 50);

View File

@@ -0,0 +1,7 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
use tests::threads::mlfqs;
check_mlfqs_fair ([(0) x 20], 20);

View File

@@ -0,0 +1,124 @@
/* Measures the correctness of the "nice" implementation.
The "fair" tests run either 2 or 20 threads all niced to 0.
The threads should all receive approximately the same number
of ticks. Each test runs for 30 seconds, so the ticks should
also sum to approximately 30 * 100 == 3000 ticks.
The mlfqs-nice-2 test runs 2 threads, one with nice 0, the
other with nice 5, which should receive 1,904 and 1,096 ticks,
respectively, over 30 seconds.
The mlfqs-nice-10 test runs 10 threads with nice 0 through 9.
They should receive 672, 588, 492, 408, 316, 232, 152, 92, 40,
and 8 ticks, respectively, over 30 seconds.
(The above are computed via simulation in mlfqs.pm.) */
#include <stdio.h>
#include <inttypes.h>
#include "tests/threads/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/palloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
static void test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step);
void
test_mlfqs_fair_2 (void)
{
test_mlfqs_fair (2, 0, 0);
}
void
test_mlfqs_fair_20 (void)
{
test_mlfqs_fair (20, 0, 0);
}
void
test_mlfqs_nice_2 (void)
{
test_mlfqs_fair (2, 0, 5);
}
void
test_mlfqs_nice_10 (void)
{
test_mlfqs_fair (10, 0, 1);
}
#define MAX_THREAD_CNT 20
struct thread_info
{
int64_t start_time;
int tick_count;
int nice;
};
static void load_thread (void *aux);
static void
test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step)
{
struct thread_info info[MAX_THREAD_CNT];
int64_t start_time;
int nice;
int i;
ASSERT (thread_mlfqs);
ASSERT (thread_cnt <= MAX_THREAD_CNT);
ASSERT (nice_min >= -10);
ASSERT (nice_step >= 0);
ASSERT (nice_min + nice_step * (thread_cnt - 1) <= 20);
thread_set_nice (-20);
start_time = timer_ticks ();
msg ("Starting %d threads...", thread_cnt);
nice = nice_min;
for (i = 0; i < thread_cnt; i++)
{
struct thread_info *ti = &info[i];
char name[16];
ti->start_time = start_time;
ti->tick_count = 0;
ti->nice = nice;
snprintf(name, sizeof name, "load %d", i);
thread_create (name, PRI_DEFAULT, load_thread, ti);
nice += nice_step;
}
msg ("Starting threads took %"PRId64" ticks.", timer_elapsed (start_time));
msg ("Sleeping 40 seconds to let threads run, please wait...");
timer_sleep (40 * TIMER_FREQ);
for (i = 0; i < thread_cnt; i++)
msg ("Thread %d received %d ticks.", i, info[i].tick_count);
}
static void
load_thread (void *ti_)
{
struct thread_info *ti = ti_;
int64_t sleep_time = 5 * TIMER_FREQ;
int64_t spin_time = sleep_time + 30 * TIMER_FREQ;
int64_t last_time = 0;
thread_set_nice (ti->nice);
timer_sleep (sleep_time - timer_elapsed (ti->start_time));
while (timer_elapsed (ti->start_time) < spin_time)
{
int64_t cur_time = timer_ticks ();
if (cur_time != last_time)
ti->tick_count++;
last_time = cur_time;
}
}

View File

@@ -0,0 +1,60 @@
/* Verifies that a single busy thread raises the load average to
0.5 in 38 to 45 seconds. The expected time is 42 seconds, as
you can verify:
perl -e '$i++,$a=(59*$a+1)/60while$a<=.5;print "$i\n"'
Then, verifies that 10 seconds of inactivity drop the load
average back below 0.5 again. */
#include <stdio.h>
#include "tests/threads/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
void
test_mlfqs_load_1 (void)
{
int64_t start_time;
int elapsed;
int load_avg;
ASSERT (thread_mlfqs);
msg ("spinning for up to 45 seconds, please wait...");
start_time = timer_ticks ();
for (;;)
{
load_avg = thread_get_load_avg ();
ASSERT (load_avg >= 0);
elapsed = (int)(timer_elapsed (start_time) / TIMER_FREQ);
if (load_avg > 100)
fail ("load average is %d.%02d "
"but should be between 0 and 1 (after %d seconds)",
load_avg / 100, load_avg % 100, elapsed);
else if (load_avg > 50)
break;
else if (elapsed > 45)
fail ("load average stayed below 0.5 for more than 45 seconds");
}
if (elapsed < 38)
fail ("load average took only %d seconds to rise above 0.5", elapsed);
msg ("load average rose to 0.5 after %d seconds", elapsed);
msg ("sleeping for another 10 seconds, please wait...");
timer_sleep (TIMER_FREQ * 10);
load_avg = thread_get_load_avg ();
if (load_avg < 0)
fail ("load average fell below 0");
if (load_avg > 50)
fail ("load average stayed above 0.5 for more than 10 seconds");
msg ("load average fell back below 0.5 (to %d.%02d)",
load_avg / 100, load_avg % 100);
pass ();
}

View File

@@ -0,0 +1,15 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
our ($test);
my (@output) = read_text_file ("$test.output");
common_checks ("run", @output);
@output = get_core_output ("run", @output);
fail "missing PASS in output"
unless grep ($_ eq '(mlfqs-load-1) PASS', @output);
pass;

View File

@@ -0,0 +1,160 @@
/* Starts 60 threads that each sleep for 10 seconds, then spin in
a tight loop for 60 seconds, and sleep for another 60 seconds.
Every 2 seconds after the initial sleep, the main thread
prints the load average.
The expected output is this (some margin of error is allowed):
After 0 seconds, load average=1.00.
After 2 seconds, load average=2.95.
After 4 seconds, load average=4.84.
After 6 seconds, load average=6.66.
After 8 seconds, load average=8.42.
After 10 seconds, load average=10.13.
After 12 seconds, load average=11.78.
After 14 seconds, load average=13.37.
After 16 seconds, load average=14.91.
After 18 seconds, load average=16.40.
After 20 seconds, load average=17.84.
After 22 seconds, load average=19.24.
After 24 seconds, load average=20.58.
After 26 seconds, load average=21.89.
After 28 seconds, load average=23.15.
After 30 seconds, load average=24.37.
After 32 seconds, load average=25.54.
After 34 seconds, load average=26.68.
After 36 seconds, load average=27.78.
After 38 seconds, load average=28.85.
After 40 seconds, load average=29.88.
After 42 seconds, load average=30.87.
After 44 seconds, load average=31.84.
After 46 seconds, load average=32.77.
After 48 seconds, load average=33.67.
After 50 seconds, load average=34.54.
After 52 seconds, load average=35.38.
After 54 seconds, load average=36.19.
After 56 seconds, load average=36.98.
After 58 seconds, load average=37.74.
After 60 seconds, load average=37.48.
After 62 seconds, load average=36.24.
After 64 seconds, load average=35.04.
After 66 seconds, load average=33.88.
After 68 seconds, load average=32.76.
After 70 seconds, load average=31.68.
After 72 seconds, load average=30.63.
After 74 seconds, load average=29.62.
After 76 seconds, load average=28.64.
After 78 seconds, load average=27.69.
After 80 seconds, load average=26.78.
After 82 seconds, load average=25.89.
After 84 seconds, load average=25.04.
After 86 seconds, load average=24.21.
After 88 seconds, load average=23.41.
After 90 seconds, load average=22.64.
After 92 seconds, load average=21.89.
After 94 seconds, load average=21.16.
After 96 seconds, load average=20.46.
After 98 seconds, load average=19.79.
After 100 seconds, load average=19.13.
After 102 seconds, load average=18.50.
After 104 seconds, load average=17.89.
After 106 seconds, load average=17.30.
After 108 seconds, load average=16.73.
After 110 seconds, load average=16.17.
After 112 seconds, load average=15.64.
After 114 seconds, load average=15.12.
After 116 seconds, load average=14.62.
After 118 seconds, load average=14.14.
After 120 seconds, load average=13.67.
After 122 seconds, load average=13.22.
After 124 seconds, load average=12.78.
After 126 seconds, load average=12.36.
After 128 seconds, load average=11.95.
After 130 seconds, load average=11.56.
After 132 seconds, load average=11.17.
After 134 seconds, load average=10.80.
After 136 seconds, load average=10.45.
After 138 seconds, load average=10.10.
After 140 seconds, load average=9.77.
After 142 seconds, load average=9.45.
After 144 seconds, load average=9.13.
After 146 seconds, load average=8.83.
After 148 seconds, load average=8.54.
After 150 seconds, load average=8.26.
After 152 seconds, load average=7.98.
After 154 seconds, load average=7.72.
After 156 seconds, load average=7.47.
After 158 seconds, load average=7.22.
After 160 seconds, load average=6.98.
After 162 seconds, load average=6.75.
After 164 seconds, load average=6.53.
After 166 seconds, load average=6.31.
After 168 seconds, load average=6.10.
After 170 seconds, load average=5.90.
After 172 seconds, load average=5.70.
After 174 seconds, load average=5.52.
After 176 seconds, load average=5.33.
After 178 seconds, load average=5.16.
*/
#include <stdio.h>
#include "tests/threads/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
static int64_t start_time;
static void load_thread (void *aux);
#define THREAD_CNT 60
void
test_mlfqs_load_60 (void)
{
int i;
ASSERT (thread_mlfqs);
start_time = timer_ticks ();
/* align start_time to TIMER_FREQ to ensure sleeping threads wake up at the
tick on which the load average is recalculated */
start_time = ((start_time / TIMER_FREQ) + 1) * TIMER_FREQ;
msg ("Starting %d niced load threads...", THREAD_CNT);
for (i = 0; i < THREAD_CNT; i++)
{
char name[16];
snprintf(name, sizeof name, "load %d", i);
thread_create (name, PRI_DEFAULT, load_thread, NULL);
}
msg ("Starting threads took %d seconds.",
timer_elapsed (start_time) / TIMER_FREQ);
for (i = 0; i < 90; i++)
{
int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
int load_avg;
timer_sleep (sleep_until - timer_ticks ());
load_avg = thread_get_load_avg ();
msg ("After %d seconds, load average=%d.%02d.",
i * 2, load_avg / 100, load_avg % 100);
}
}
static void
load_thread (void *aux UNUSED)
{
int64_t sleep_time = 10 * TIMER_FREQ;
int64_t spin_time = sleep_time + 60 * TIMER_FREQ;
int64_t exit_time = spin_time + 60 * TIMER_FREQ;
thread_set_nice (20);
timer_sleep (sleep_time - timer_elapsed (start_time));
while (timer_elapsed (start_time) < spin_time)
continue;
timer_sleep (exit_time - timer_elapsed (start_time));
}

View File

@@ -0,0 +1,36 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
use tests::threads::mlfqs;
our ($test);
my (@output) = read_text_file ("$test.output");
common_checks ("run", @output);
@output = get_core_output ("run", @output);
# Get actual values.
local ($_);
my (@actual);
foreach (@output) {
my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
or next;
$actual[$t] = $load_avg;
}
# Calculate expected values.
my ($load_avg) = 0;
my ($recent) = 0;
my (@expected);
for (my ($t) = 0; $t < 180; $t++) {
my ($ready) = $t < 60 ? 60 : 0;
$load_avg = (59/60) * $load_avg + (1/60) * $ready;
$expected[$t] = $load_avg;
}
mlfqs_compare ("time", "%.2f", \@actual, \@expected, 3.5, [2, 178, 2],
"Some load average values were missing or "
. "differed from those expected "
. "by more than 3.5.");
pass;

View File

@@ -0,0 +1,167 @@
/* Starts 60 threads numbered 0 through 59. Thread #i sleeps for
(10+i) seconds, then spins in a loop for 60 seconds, then
sleeps until a total of 120 seconds have passed. Every 2
seconds, starting 10 seconds in, the main thread prints the
load average.
The expected output is listed below. Some margin of error is
allowed.
If your implementation fails this test but passes most other
tests, then consider whether you are doing too much work in
the timer interrupt. If the timer interrupt handler takes too
long, then the test's main thread will not have enough time to
do its own work (printing a message) and go back to sleep
before the next tick arrives. Then the main thread will be
ready, instead of sleeping, when the tick arrives,
artificially driving up the load average.
After 0 seconds, load average=0.00.
After 2 seconds, load average=0.05.
After 4 seconds, load average=0.16.
After 6 seconds, load average=0.34.
After 8 seconds, load average=0.58.
After 10 seconds, load average=0.87.
After 12 seconds, load average=1.22.
After 14 seconds, load average=1.63.
After 16 seconds, load average=2.09.
After 18 seconds, load average=2.60.
After 20 seconds, load average=3.16.
After 22 seconds, load average=3.76.
After 24 seconds, load average=4.42.
After 26 seconds, load average=5.11.
After 28 seconds, load average=5.85.
After 30 seconds, load average=6.63.
After 32 seconds, load average=7.46.
After 34 seconds, load average=8.32.
After 36 seconds, load average=9.22.
After 38 seconds, load average=10.15.
After 40 seconds, load average=11.12.
After 42 seconds, load average=12.13.
After 44 seconds, load average=13.16.
After 46 seconds, load average=14.23.
After 48 seconds, load average=15.33.
After 50 seconds, load average=16.46.
After 52 seconds, load average=17.62.
After 54 seconds, load average=18.81.
After 56 seconds, load average=20.02.
After 58 seconds, load average=21.26.
After 60 seconds, load average=22.52.
After 62 seconds, load average=23.71.
After 64 seconds, load average=24.80.
After 66 seconds, load average=25.78.
After 68 seconds, load average=26.66.
After 70 seconds, load average=27.45.
After 72 seconds, load average=28.14.
After 74 seconds, load average=28.75.
After 76 seconds, load average=29.27.
After 78 seconds, load average=29.71.
After 80 seconds, load average=30.06.
After 82 seconds, load average=30.34.
After 84 seconds, load average=30.55.
After 86 seconds, load average=30.68.
After 88 seconds, load average=30.74.
After 90 seconds, load average=30.73.
After 92 seconds, load average=30.66.
After 94 seconds, load average=30.52.
After 96 seconds, load average=30.32.
After 98 seconds, load average=30.06.
After 100 seconds, load average=29.74.
After 102 seconds, load average=29.37.
After 104 seconds, load average=28.95.
After 106 seconds, load average=28.47.
After 108 seconds, load average=27.94.
After 110 seconds, load average=27.36.
After 112 seconds, load average=26.74.
After 114 seconds, load average=26.07.
After 116 seconds, load average=25.36.
After 118 seconds, load average=24.60.
After 120 seconds, load average=23.81.
After 122 seconds, load average=23.02.
After 124 seconds, load average=22.26.
After 126 seconds, load average=21.52.
After 128 seconds, load average=20.81.
After 130 seconds, load average=20.12.
After 132 seconds, load average=19.46.
After 134 seconds, load average=18.81.
After 136 seconds, load average=18.19.
After 138 seconds, load average=17.59.
After 140 seconds, load average=17.01.
After 142 seconds, load average=16.45.
After 144 seconds, load average=15.90.
After 146 seconds, load average=15.38.
After 148 seconds, load average=14.87.
After 150 seconds, load average=14.38.
After 152 seconds, load average=13.90.
After 154 seconds, load average=13.44.
After 156 seconds, load average=13.00.
After 158 seconds, load average=12.57.
After 160 seconds, load average=12.15.
After 162 seconds, load average=11.75.
After 164 seconds, load average=11.36.
After 166 seconds, load average=10.99.
After 168 seconds, load average=10.62.
After 170 seconds, load average=10.27.
After 172 seconds, load average=9.93.
After 174 seconds, load average=9.61.
After 176 seconds, load average=9.29.
After 178 seconds, load average=8.98.
*/
#include <stdio.h>
#include "tests/threads/tests.h"
#include "threads/init.h"
#include "threads/malloc.h"
#include "threads/synch.h"
#include "threads/thread.h"
#include "devices/timer.h"
static int64_t start_time;
static void load_thread (void *seq_no);
#define THREAD_CNT 60
void
test_mlfqs_load_avg (void)
{
int i;
ASSERT (thread_mlfqs);
start_time = timer_ticks ();
msg ("Starting %d load threads...", THREAD_CNT);
for (i = 0; i < THREAD_CNT; i++)
{
char name[16];
snprintf(name, sizeof name, "load %d", i);
thread_create (name, PRI_DEFAULT, load_thread, (void *) i);
}
msg ("Starting threads took %d seconds.",
timer_elapsed (start_time) / TIMER_FREQ);
thread_set_nice (-20);
for (i = 0; i < 90; i++)
{
int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
int load_avg;
timer_sleep (sleep_until - timer_ticks ());
load_avg = thread_get_load_avg ();
msg ("After %d seconds, load average=%d.%02d.",
i * 2, load_avg / 100, load_avg % 100);
}
}
static void
load_thread (void *seq_no_)
{
int seq_no = (int) seq_no_;
int sleep_time = TIMER_FREQ * (10 + seq_no);
int spin_time = sleep_time + TIMER_FREQ * THREAD_CNT;
int exit_time = TIMER_FREQ * (THREAD_CNT * 2);
timer_sleep (sleep_time - timer_elapsed (start_time));
while (timer_elapsed (start_time) < spin_time)
continue;
timer_sleep (exit_time - timer_elapsed (start_time));
}

View File

@@ -0,0 +1,36 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
use tests::threads::mlfqs;
our ($test);
my (@output) = read_text_file ("$test.output");
common_checks ("run", @output);
@output = get_core_output ("run", @output);
# Get actual values.
local ($_);
my (@actual);
foreach (@output) {
my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
or next;
$actual[$t] = $load_avg;
}
# Calculate expected values.
my ($load_avg) = 0;
my ($recent) = 0;
my (@expected);
for (my ($t) = 0; $t < 180; $t++) {
my ($ready) = $t < 60 ? $t : $t < 120 ? 120 - $t : 0;
$load_avg = (59/60) * $load_avg + (1/60) * $ready;
$expected[$t] = $load_avg;
}
mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2],
"Some load average values were missing or "
. "differed from those expected "
. "by more than 2.5.");
pass;

View File

@@ -0,0 +1,7 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
use tests::threads::mlfqs;
check_mlfqs_fair ([0...9], 25);

View File

@@ -0,0 +1,7 @@
# -*- perl -*-
use strict;
use warnings;
use tests::tests;
use tests::threads::mlfqs;
check_mlfqs_fair ([0, 5], 50);

Some files were not shown because too many files have changed in this diff Show More