2 * Recursively executes itself until the child fails to execute.
3 * We expect that at least 30 copies can run.
5 * We count how many children your kernel was able to execute
6 * before it fails to start a new process. We require that,
7 * if a process doesn't actually get to start, exec() must
8 * return -1, not a valid PID.
10 * We repeat this process 10 times, checking that your kernel
11 * allows for the same level of depth every time.
13 * In addition, some processes will spawn children that terminate
14 * abnormally after allocating some resources.
16 * Written by Godmar Back <godmar@gmail.com>
26 #include "tests/lib.h"
28 static const int EXPECTED_DEPTH_TO_PASS = 30;
29 static const int EXPECTED_REPETITIONS = 10;
31 const char *test_name = "multi-oom";
33 enum child_termination_mode { RECURSE, CRASH };
35 /* Spawn a recursive copy of ourselves, passing along instructions
38 spawn_child (int c, enum child_termination_mode mode)
41 snprintf (child_cmd, sizeof child_cmd,
42 "%s %d %s", test_name, c, mode == CRASH ? "-k" : "");
43 return exec (child_cmd);
46 /* Open a number of files (and fail to close them.)
47 * The kernel must free any kernel resources associated
48 * with these file descriptors. */
50 consume_some_resources (void)
54 /* Open as many files as we can, up to fdmax.
55 * Depending on how file descriptors are allocated inside
56 * the kernel, open() may fail if the kernel is low on memory.
57 * A low-memory condition in open() should not lead to the
58 * termination of the process. */
59 for (fd = 0; fd < fdmax; fd++)
60 if (open (test_name) == -1)
64 /* Consume some resources, then terminate this process
65 * in some abnormal way. */
67 consume_some_resources_and_die (int seed)
69 consume_some_resources ();
71 int * PHYS_BASE = (int *)0xC0000000;
73 switch (random_ulong () % 5)
79 return * (int *) NULL;
88 open ((char *)PHYS_BASE);
97 /* The first copy is invoked without command line arguments.
98 * Subsequent copies are invoked with a parameter 'depth'
99 * that describes how many parent processes preceded them.
100 * Each process spawns one or multiple recursive copies of
101 * itself, passing 'depth+1' as depth.
103 * Some children are started with the '-k' flag, which will
104 * result in abnormal termination.
107 main (int argc, char *argv[])
111 n = argc > 1 ? atoi (argv[1]) : 0;
112 bool is_at_root = (n == 0);
116 /* if -k is passed, crash this process. */
117 if (argc > 2 && !strcmp(argv[2], "-k"))
119 consume_some_resources_and_die (n);
123 int howmany = is_at_root ? EXPECTED_REPETITIONS : 1;
124 int i, expected_depth = -1;
126 for (i = 0; i < howmany; i++)
130 /* Spawn a child that will be abnormally terminated.
131 * To speed the test up, do this only for processes
132 * spawned at a certain depth. */
133 if (n > EXPECTED_DEPTH_TO_PASS/2)
135 child_pid = spawn_child (n + 1, CRASH);
138 if (wait (child_pid) != -1)
139 fail ("crashed child should return -1.");
141 /* if spawning this child failed, so should
142 * the next spawn_child below. */
145 /* Now spawn the child that will recurse. */
146 child_pid = spawn_child (n + 1, RECURSE);
148 /* If maximum depth is reached, return result. */
152 /* Else wait for child to report how deeply it was able to recurse. */
153 int reached_depth = wait (child_pid);
154 if (reached_depth == -1)
155 fail ("wait returned -1.");
157 /* Record the depth reached during the first run; on subsequent
158 * runs, fail if those runs do not match the depth achieved on the
161 expected_depth = reached_depth;
162 else if (expected_depth != reached_depth)
164 fail ("after run %d/%d, expected depth %d, actual depth %d.",
165 i, howmany, expected_depth, reached_depth);
167 ASSERT (expected_depth == reached_depth);
170 consume_some_resources ();
174 if (expected_depth < EXPECTED_DEPTH_TO_PASS)
175 fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS);
176 msg ("success. program forked %d times.", howmany);
180 return expected_depth;