CS318 - Pintos
Pintos source browser for JHU CS318 course
multi-oom.c
Go to the documentation of this file.
1 /** Recursively executes itself until the child fails to execute.
2  We expect that at least 30 copies can run.
3 
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.
8 
9  We repeat this process 10 times, checking that your kernel
10  allows for the same level of depth every time.
11 
12  In addition, some processes will spawn children that terminate
13  abnormally after allocating some resources.
14 
15  Written by Godmar Back <godmar@gmail.com>
16  */
17 
18 #include <debug.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <stdbool.h>
23 #include <syscall.h>
24 #include <random.h>
25 #include "tests/lib.h"
26 
27 static const int EXPECTED_DEPTH_TO_PASS = 30;
28 static const int EXPECTED_REPETITIONS = 10;
29 
30 const char *test_name = "multi-oom";
31 
33 
34 /** Spawn a recursive copy of ourselves, passing along instructions
35  for the child. */
36 static pid_t
38 {
39  char child_cmd[128];
40  snprintf (child_cmd, sizeof child_cmd,
41  "%s %d %s", test_name, c, mode == CRASH ? "-k" : "");
42  return exec (child_cmd);
43 }
44 
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. */
48 static void
50 {
51  int fd, fdmax = 126;
52 
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)
60  break;
61 }
62 
63 /** Consume some resources, then terminate this process
64  in some abnormal way. */
65 static int NO_INLINE
67 {
69  random_init (seed);
70  volatile int *PHYS_BASE = (volatile int *)0xC0000000;
71 
72  switch (random_ulong () % 5)
73  {
74  case 0:
75  *(volatile int *) NULL = 42;
76 
77  case 1:
78  return *(volatile int *) NULL;
79 
80  case 2:
81  return *PHYS_BASE;
82 
83  case 3:
84  *PHYS_BASE = 42;
85 
86  case 4:
87  open ((char *)PHYS_BASE);
88  exit (-1);
89 
90  default:
91  NOT_REACHED ();
92  }
93  return 0;
94 }
95 
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.
101 
102  Some children are started with the '-k' flag, which will
103  result in abnormal termination.
104  */
105 int
106 main (int argc, char *argv[])
107 {
108  int n;
109 
110  n = argc > 1 ? atoi (argv[1]) : 0;
111  bool is_at_root = (n == 0);
112  if (is_at_root)
113  msg ("begin");
114 
115  /* If -k is passed, crash this process. */
116  if (argc > 2 && !strcmp(argv[2], "-k"))
117  {
119  NOT_REACHED ();
120  }
121 
122  int howmany = is_at_root ? EXPECTED_REPETITIONS : 1;
123  int i, expected_depth = -1;
124 
125  for (i = 0; i < howmany; i++)
126  {
127  pid_t child_pid;
128 
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)
133  {
134  child_pid = spawn_child (n + 1, CRASH);
135  if (child_pid != -1)
136  {
137  if (wait (child_pid) != -1)
138  fail ("crashed child should return -1.");
139  }
140  /* If spawning this child failed, so should
141  the next spawn_child below. */
142  }
143 
144  /* Now spawn the child that will recurse. */
145  child_pid = spawn_child (n + 1, RECURSE);
146 
147  /* If maximum depth is reached, return result. */
148  if (child_pid == -1)
149  return n;
150 
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.");
155 
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
158  first run. */
159  if (i == 0)
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);
165  }
166 
168 
169  if (n == 0)
170  {
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);
174  msg ("end");
175  }
176 
177  return expected_depth;
178 }
179 // vim: sw=2
lib.h
snprintf
int snprintf(char *buffer, size_t buf_size, const char *format,...)
Like printf(), except that output is stored into BUFFER, which must have space for BUF_SIZE character...
Definition: stdio.c:62
CRASH
Definition: multi-oom.c:32
PHYS_BASE
#define PHYS_BASE
Base address of the 1:1 physical-to-virtual mapping.
Definition: vaddr.h:53
mode
static enum @0 mode
Transmission mode.
NULL
#define NULL
Definition: stddef.h:4
NO_INLINE
#define NO_INLINE
Definition: debug.h:9
string.h
test_name
const char * test_name
Child process for syn-read test.
Definition: multi-oom.c:30
child_termination_mode
child_termination_mode
Definition: multi-oom.c:32
strcmp
int strcmp(const char *a_, const char *b_)
Finds the first differing characters in strings A and B.
Definition: string.c:73
main
int main(int argc, char *argv[])
The first copy is invoked without command line arguments.
Definition: multi-oom.c:106
atoi
int atoi(const char *s)
Converts a string representation of a signed decimal integer in S into an ‘int’, which is returned.
Definition: stdlib.c:10
random_ulong
unsigned long random_ulong(void)
Returns a pseudo-random unsigned long.
Definition: random.c:78
exec
pid_t exec(const char *file)
Definition: syscall.c:79
stdbool.h
NOT_REACHED
#define NOT_REACHED()
lib/debug.h
Definition: debug.h:35
random.h
consume_some_resources
static void consume_some_resources(void)
Open a number of files (and fail to close them).
Definition: multi-oom.c:49
random_init
void random_init(unsigned seed)
Initializes or reinitializes the PRNG with the given SEED.
Definition: random.c:34
open
int open(const char *file)
Definition: syscall.c:103
consume_some_resources_and_die
static int NO_INLINE consume_some_resources_and_die(int seed)
Consume some resources, then terminate this process in some abnormal way.
Definition: multi-oom.c:66
EXPECTED_REPETITIONS
static const int EXPECTED_REPETITIONS
Definition: multi-oom.c:28
ASSERT
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
fail
void fail(const char *format,...)
Definition: lib.c:40
spawn_child
static pid_t spawn_child(int c, enum child_termination_mode mode)
Spawn a recursive copy of ourselves, passing along instructions for the child.
Definition: multi-oom.c:37
RECURSE
Definition: multi-oom.c:32
EXPECTED_DEPTH_TO_PASS
static const int EXPECTED_DEPTH_TO_PASS
Recursively executes itself until the child fails to execute.
Definition: multi-oom.c:27
wait
static void wait(struct intq *q, struct thread **waiter)
pid_t
int pid_t
Process identifier.
Definition: syscall.h:8
msg
void msg(const char *format,...)
Definition: lib.c:28
stdlib.h
exit
void exit(int status)
Definition: syscall.c:72
debug.h