75cad149d912ab82f05bb8b72235aaa32b01497c
[pintos-anon] / src / tests / userprog / no-vm / multi-oom.c
1 /* 
2  * Recursively executes itself until the child fails to execute.
3  * We expect that at least 30 copies can run.
4  *
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. 
9  *
10  * We repeat this process 10 times, checking that your kernel
11  * allows for the same level of depth every time.
12  *
13  * In addition, some processes will spawn children that terminate
14  * abnormally after allocating some resources.
15  *
16  * Written by Godmar Back <godmar@gmail.com>
17  */
18
19 #include <debug.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
24 #include <syscall.h>
25 #include <random.h>
26 #include "tests/lib.h"
27
28 static const int EXPECTED_DEPTH_TO_PASS = 30;
29 static const int EXPECTED_REPETITIONS = 10;
30
31 const char *test_name = "multi-oom";
32
33 enum child_termination_mode { RECURSE, CRASH };
34
35 /* Spawn a recursive copy of ourselves, passing along instructions
36  * for the child. */
37 static pid_t
38 spawn_child (int c, enum child_termination_mode mode)
39 {
40   char child_cmd[128];
41   snprintf (child_cmd, sizeof child_cmd, 
42             "%s %d %s", test_name, c, mode == CRASH ? "-k" : "");
43   return exec (child_cmd);
44 }
45
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. */
49 static void
50 consume_some_resources (void)
51 {
52   int fd, fdmax = 126;
53
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)
61       break;
62 }
63
64 /* Consume some resources, then terminate this process
65  * in some abnormal way.  */
66 static int NO_INLINE
67 consume_some_resources_and_die (int seed)
68 {
69   consume_some_resources ();
70   random_init (seed);
71   int * PHYS_BASE = (int *)0xC0000000;
72
73   switch (random_ulong () % 5)
74     {
75       case 0:
76         * (int *) NULL = 42;
77
78       case 1:
79         return * (int *) NULL;
80
81       case 2:
82         return *PHYS_BASE;
83
84       case 3:
85         *PHYS_BASE = 42;
86
87       case 4:
88         open ((char *)PHYS_BASE);
89         exit (-1);
90
91       default:
92         NOT_REACHED ();
93     }
94   return 0;
95 }
96
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.
102  *
103  * Some children are started with the '-k' flag, which will
104  * result in abnormal termination.
105  */ 
106 int
107 main (int argc, char *argv[]) 
108 {
109   int n;
110
111   n = argc > 1 ? atoi (argv[1]) : 0;
112   bool is_at_root = (n == 0);
113   if (is_at_root)
114     msg ("begin");
115
116   /* if -k is passed, crash this process. */
117   if (argc > 2 && !strcmp(argv[2], "-k")) 
118     {
119       consume_some_resources_and_die (n);
120       NOT_REACHED ();
121     }
122
123   int howmany = is_at_root ? EXPECTED_REPETITIONS : 1;
124   int i, expected_depth = -1;
125
126   for (i = 0; i < howmany; i++)
127     {
128       pid_t child_pid;
129
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)
134         {
135           child_pid = spawn_child (n + 1, CRASH);
136           if (child_pid != -1) 
137             {
138               if (wait (child_pid) != -1)
139                 fail ("crashed child should return -1.");
140             }
141           /* if spawning this child failed, so should
142            * the next spawn_child below. */
143         }
144
145       /* Now spawn the child that will recurse. */
146       child_pid = spawn_child (n + 1, RECURSE);
147
148       /* If maximum depth is reached, return result. */
149       if (child_pid == -1) 
150         return n;       
151
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.");
156
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
159        * first run. */
160       if (i == 0)
161         expected_depth = reached_depth;
162       else if (expected_depth != reached_depth)
163         {
164           fail ("after run %d/%d, expected depth %d, actual depth %d.", 
165                  i, howmany, expected_depth, reached_depth);
166         }
167       ASSERT (expected_depth == reached_depth);
168     }
169
170   consume_some_resources ();
171
172   if (n == 0)
173     {
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);
177       msg ("end");
178     }
179
180   return expected_depth;
181 }
182 // vim: sw=2