Rewrite of multi-oom. Now descends 10 times and expects to reach the same
authorGodmar Back <godmar@gmail.com>
Fri, 10 Nov 2006 04:36:14 +0000 (04:36 +0000)
committerGodmar Back <godmar@gmail.com>
Fri, 10 Nov 2006 04:36:14 +0000 (04:36 +0000)
depth every time.  In addition, fires off some children that consume resources
and then crash.  Much more difficult than before.

src/tests/userprog/no-vm/Make.tests
src/tests/userprog/no-vm/multi-oom.c
src/tests/userprog/no-vm/multi-oom.ck

index 7aaa53af705a0ed64bf05eab4bd0736c00102baf..a545e18eff1f159522e2ad9fb719279e176a85ca 100644 (file)
@@ -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
index d4d50a64a7370ac9bd4ed0e0c3fbf3a931049e16..75cad149d912ab82f05bb8b72235aaa32b01497c 100644 (file)
-/* 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 <godmar@gmail.com>
+ */
 
 #include <debug.h>
 #include <stdio.h>
+#include <string.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <syscall.h>
+#include <random.h>
 #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
index 7ed5cc86defbe41c3c2d5658269cd226097a9f7f..59a0bcd5324c899fc093a7fd24505d615d4ba778 100644 (file)
@@ -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;