From: Godmar Back Date: Fri, 10 Nov 2006 04:36:14 +0000 (+0000) Subject: Rewrite of multi-oom. Now descends 10 times and expects to reach the same X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2f9044505b2103fb0f165341fcbeb3104289572e;p=pintos-anon Rewrite of multi-oom. Now descends 10 times and expects to reach the same depth every time. In addition, fires off some children that consume resources and then crash. Much more difficult than before. --- diff --git a/src/tests/userprog/no-vm/Make.tests b/src/tests/userprog/no-vm/Make.tests index 7aaa53a..a545e18 100644 --- a/src/tests/userprog/no-vm/Make.tests +++ b/src/tests/userprog/no-vm/Make.tests @@ -4,6 +4,5 @@ tests/userprog/no-vm_TESTS = tests/userprog/no-vm/multi-oom tests/userprog/no-vm_PROGS = $(tests/userprog/no-vm_TESTS) tests/userprog/no-vm/multi-oom_SRC = tests/userprog/no-vm/multi-oom.c \ tests/lib.c -tests/userprog/no-vm/multi-oom_ARGS = 0 -tests/userprog/no-vm/multi-oom.output: TIMEOUT = 180 +tests/userprog/no-vm/multi-oom.output: TIMEOUT = 360 diff --git a/src/tests/userprog/no-vm/multi-oom.c b/src/tests/userprog/no-vm/multi-oom.c index d4d50a6..75cad14 100644 --- a/src/tests/userprog/no-vm/multi-oom.c +++ b/src/tests/userprog/no-vm/multi-oom.c @@ -1,40 +1,182 @@ -/* Recursively executes itself until the child fails to execute. - We expect that at least 15 copies can run. - We also require that, if a process doesn't actually get to - start, exec() must return -1, not a valid PID. */ +/* + * Recursively executes itself until the child fails to execute. + * We expect that at least 30 copies can run. + * + * We count how many children your kernel was able to execute + * before it fails to start a new process. We require that, + * if a process doesn't actually get to start, exec() must + * return -1, not a valid PID. + * + * We repeat this process 10 times, checking that your kernel + * allows for the same level of depth every time. + * + * In addition, some processes will spawn children that terminate + * abnormally after allocating some resources. + * + * Written by Godmar Back + */ #include #include +#include #include +#include #include +#include #include "tests/lib.h" +static const int EXPECTED_DEPTH_TO_PASS = 30; +static const int EXPECTED_REPETITIONS = 10; + const char *test_name = "multi-oom"; -int -main (int argc UNUSED, char *argv[]) +enum child_termination_mode { RECURSE, CRASH }; + +/* Spawn a recursive copy of ourselves, passing along instructions + * for the child. */ +static pid_t +spawn_child (int c, enum child_termination_mode mode) { char child_cmd[128]; - pid_t child_pid; + snprintf (child_cmd, sizeof child_cmd, + "%s %d %s", test_name, c, mode == CRASH ? "-k" : ""); + return exec (child_cmd); +} + +/* Open a number of files (and fail to close them.) + * The kernel must free any kernel resources associated + * with these file descriptors. */ +static void +consume_some_resources (void) +{ + int fd, fdmax = 126; + + /* Open as many files as we can, up to fdmax. + * Depending on how file descriptors are allocated inside + * the kernel, open() may fail if the kernel is low on memory. + * A low-memory condition in open() should not lead to the + * termination of the process. */ + for (fd = 0; fd < fdmax; fd++) + if (open (test_name) == -1) + break; +} + +/* Consume some resources, then terminate this process + * in some abnormal way. */ +static int NO_INLINE +consume_some_resources_and_die (int seed) +{ + consume_some_resources (); + random_init (seed); + int * PHYS_BASE = (int *)0xC0000000; + + switch (random_ulong () % 5) + { + case 0: + * (int *) NULL = 42; + + case 1: + return * (int *) NULL; + + case 2: + return *PHYS_BASE; + + case 3: + *PHYS_BASE = 42; + + case 4: + open ((char *)PHYS_BASE); + exit (-1); + + default: + NOT_REACHED (); + } + return 0; +} + +/* The first copy is invoked without command line arguments. + * Subsequent copies are invoked with a parameter 'depth' + * that describes how many parent processes preceded them. + * Each process spawns one or multiple recursive copies of + * itself, passing 'depth+1' as depth. + * + * Some children are started with the '-k' flag, which will + * result in abnormal termination. + */ +int +main (int argc, char *argv[]) +{ int n; - n = atoi (argv[1]); - if (n == 0) - n = atoi (argv[0]); - - msg ("begin %d", n); + n = argc > 1 ? atoi (argv[1]) : 0; + bool is_at_root = (n == 0); + if (is_at_root) + msg ("begin"); - snprintf (child_cmd, sizeof child_cmd, "multi-oom %d", n + 1); - child_pid = exec (child_cmd); - if (child_pid != -1) + /* if -k is passed, crash this process. */ + if (argc > 2 && !strcmp(argv[2], "-k")) { - int code = wait (child_pid); - if (code != n + 1) - fail ("wait(exec(\"%s\")) returned %d", child_cmd, code); + consume_some_resources_and_die (n); + NOT_REACHED (); } - else if (n < 15) - fail ("exec(\"%s\") returned -1 after only %d recursions", child_cmd, n); - - msg ("end %d", n); - return n; + + int howmany = is_at_root ? EXPECTED_REPETITIONS : 1; + int i, expected_depth = -1; + + for (i = 0; i < howmany; i++) + { + pid_t child_pid; + + /* Spawn a child that will be abnormally terminated. + * To speed the test up, do this only for processes + * spawned at a certain depth. */ + if (n > EXPECTED_DEPTH_TO_PASS/2) + { + child_pid = spawn_child (n + 1, CRASH); + if (child_pid != -1) + { + if (wait (child_pid) != -1) + fail ("crashed child should return -1."); + } + /* if spawning this child failed, so should + * the next spawn_child below. */ + } + + /* Now spawn the child that will recurse. */ + child_pid = spawn_child (n + 1, RECURSE); + + /* If maximum depth is reached, return result. */ + if (child_pid == -1) + return n; + + /* Else wait for child to report how deeply it was able to recurse. */ + int reached_depth = wait (child_pid); + if (reached_depth == -1) + fail ("wait returned -1."); + + /* Record the depth reached during the first run; on subsequent + * runs, fail if those runs do not match the depth achieved on the + * first run. */ + if (i == 0) + expected_depth = reached_depth; + else if (expected_depth != reached_depth) + { + fail ("after run %d/%d, expected depth %d, actual depth %d.", + i, howmany, expected_depth, reached_depth); + } + ASSERT (expected_depth == reached_depth); + } + + consume_some_resources (); + + if (n == 0) + { + if (expected_depth < EXPECTED_DEPTH_TO_PASS) + fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS); + msg ("success. program forked %d times.", howmany); + msg ("end"); + } + + return expected_depth; } +// vim: sw=2 diff --git a/src/tests/userprog/no-vm/multi-oom.ck b/src/tests/userprog/no-vm/multi-oom.ck index 7ed5cc8..59a0bcd 100644 --- a/src/tests/userprog/no-vm/multi-oom.ck +++ b/src/tests/userprog/no-vm/multi-oom.ck @@ -2,42 +2,9 @@ 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); -my ($n) = 0; -while (my ($m) = $output[0] =~ /^\(multi-oom\) begin (\d+)$/) { - fail "Child process $m started out of order.\n" if $m != $n; - $n = $m + 1; - shift @output; -} -fail "Only $n child process(es) started.\n" if $n < 15; - -# There could be a death notice for a process that didn't get -# fully loaded, and/or notices from the loader. -while (@output > 0 - && ($output[0] =~ /^multi-oom: exit\(-1\)$/ - || $output[0] =~ /^load: /)) { - shift @output; -} - -while (--$n >= 0) { - fail "Output ended unexpectedly before process $n finished.\n" - if @output < 2; - - local ($_); - chomp ($_ = shift @output); - fail "Found '$_' expecting 'end' message.\n" if !/^\(multi-oom\) end/; - fail "Child process $n ended out of order.\n" - if !/^\(multi-oom\) end $n$/; - - chomp ($_ = shift @output); - fail "Kernel didn't print proper exit message for process $n.\n" - if !/^multi-oom: exit\($n\)$/; -} -fail "Spurious output at end: '$output[0]'.\n" if @output; - +check_expected (IGNORE_USER_FAULTS => 1, IGNORE_EXIT_CODES => 1, [<<'EOF']); +(multi-oom) begin +(multi-oom) success. program forked 10 times. +(multi-oom) end +EOF pass;