1 /* Recursively executes itself until the child fails to execute.
2 We expect that at least 30 copies can run.
4 We count how many children your kernel was able to execute
5 before it fails to start a new process. We require that,
6 if a process doesn't actually get to start, exec() must
7 return -1, not a valid PID.
9 We repeat this process 10 times, checking that your kernel
10 allows for the same level of depth every time.
12 In addition, some processes will spawn children that terminate
13 abnormally after allocating some resources.
15 Written by Godmar Back <godmar@gmail.com>
25 #include "tests/lib.h"
27 static const int EXPECTED_DEPTH_TO_PASS = 30;
28 static const int EXPECTED_REPETITIONS = 10;
30 const char *test_name = "multi-oom";
32 enum child_termination_mode { RECURSE, CRASH };
34 /* Spawn a recursive copy of ourselves, passing along instructions
37 spawn_child (int c, enum child_termination_mode mode)
40 snprintf (child_cmd, sizeof child_cmd,
41 "%s %d %s", test_name, c, mode == CRASH ? "-k" : "");
42 return exec (child_cmd);
45 /* Open a number of files (and fail to close them).
46 The kernel must free any kernel resources associated
47 with these file descriptors. */
49 consume_some_resources (void)
53 /* Open as many files as we can, up to fdmax.
54 Depending on how file descriptors are allocated inside
55 the kernel, open() may fail if the kernel is low on memory.
56 A low-memory condition in open() should not lead to the
57 termination of the process. */
58 for (fd = 0; fd < fdmax; fd++)
59 if (open (test_name) == -1)
63 /* Consume some resources, then terminate this process
64 in some abnormal way. */
66 consume_some_resources_and_die (int seed)
68 consume_some_resources ();
70 int *PHYS_BASE = (int *)0xC0000000;
72 switch (random_ulong () % 5)
87 open ((char *)PHYS_BASE);
96 /* The first copy is invoked without command line arguments.
97 Subsequent copies are invoked with a parameter 'depth'
98 that describes how many parent processes preceded them.
99 Each process spawns one or multiple recursive copies of
100 itself, passing 'depth+1' as depth.
102 Some children are started with the '-k' flag, which will
103 result in abnormal termination.
106 main (int argc, char *argv[])
110 n = argc > 1 ? atoi (argv[1]) : 0;
111 bool is_at_root = (n == 0);
115 /* If -k is passed, crash this process. */
116 if (argc > 2 && !strcmp(argv[2], "-k"))
118 consume_some_resources_and_die (n);
122 int howmany = is_at_root ? EXPECTED_REPETITIONS : 1;
123 int i, expected_depth = -1;
125 for (i = 0; i < howmany; i++)
129 /* Spawn a child that will be abnormally terminated.
130 To speed the test up, do this only for processes
131 spawned at a certain depth. */
132 if (n > EXPECTED_DEPTH_TO_PASS/2)
134 child_pid = spawn_child (n + 1, CRASH);
137 if (wait (child_pid) != -1)
138 fail ("crashed child should return -1.");
140 /* If spawning this child failed, so should
141 the next spawn_child below. */
144 /* Now spawn the child that will recurse. */
145 child_pid = spawn_child (n + 1, RECURSE);
147 /* If maximum depth is reached, return result. */
151 /* Else wait for child to report how deeply it was able to recurse. */
152 int reached_depth = wait (child_pid);
153 if (reached_depth == -1)
154 fail ("wait returned -1.");
156 /* Record the depth reached during the first run; on subsequent
157 runs, fail if those runs do not match the depth achieved on the
160 expected_depth = reached_depth;
161 else if (expected_depth != reached_depth)
162 fail ("after run %d/%d, expected depth %d, actual depth %d.",
163 i, howmany, expected_depth, reached_depth);
164 ASSERT (expected_depth == reached_depth);
167 consume_some_resources ();
171 if (expected_depth < EXPECTED_DEPTH_TO_PASS)
172 fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS);
173 msg ("success. program forked %d times.", howmany);
177 return expected_depth;