xv6, line by line
user/grind.c

user/grind.c

C · 351 lines · annotated 100% · user program / library · upstream

About this file

grind is xv6’s randomized stress test: it runs random system calls in two processes at once, forever. Each process repeatedly draws a pseudo-random number and performs one of 22 operations: create, open, unlink and link files with odd path names such as /./grindir/./../b, make and remove directories, read and write, fork (with and without waiting), kill, grow and shrink memory, use pipes, and run a small echo hi | cat pipeline. The two processes share the root directory and the names a, b and c, so their operations collide in orders no hand-written test would try.

Most operations are allowed to fail (unlinking a name the other process already removed is fine). What grind checks is that the kernel survives: no panic, no hang, and a few things that must always work still do (a fork succeeds, a freshly created file has the size that was written, the pipeline prints hi). It was added in 2019 (commit 16b3b63, “grind: run parallel system calls forever”).

The structure has three levels: main starts an iteration (iter) in a child process, iter forks the two workers (go), and each worker loops forever. Workers only exit when a check fails, so with a correct kernel the first iteration never ends and you see only a growing line of A and B (one letter per 500 operations of each worker). A failure prints a grind: … message, after which a new iteration starts with a different random seed. Stop it by quitting QEMU.

Read before: user/usertests.c, whose tests are deterministic. Read next: the kernel paths it exercises: kernel/sysfile.c, kernel/fs.c, kernel/proc.c, kernel/pipe.c.

1//
2// run random system calls in parallel forever.
3//
15// from FreeBSD.
16int
17do_rand(unsigned long *ctx)
19 /*
20 * Compute x = (7^5 * x) mod (2^31 - 1)
21 * without overflowing 31 bits:
22 * (2^31 - 1) = 127773 * (7^5) + 2836
23 * From "Random number generators: good ones are hard to find",
24 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
25 * October 1988, p. 1195.
26 */
27 long hi, lo, x;
29 /* Transform to [1, 0x7ffffffe] range. */
30 x = (*ctx % 0x7ffffffe) + 1;
31 hi = x / 127773;
32 lo = x % 127773;
33 x = 16807 * lo - 2836 * hi;
34 if (x < 0)
35 x += 0x7fffffff;
36 /* Transform to [0, 0x7ffffffd] range. */
37 x--;
38 *ctx = x;
39 return (x);
42unsigned long rand_next = 1;
44int
45rand(void)
47 return (do_rand(&rand_next));
50void
53 int fd = -1;
54 static char buf[999];
55 char *break0 = sbrk(0);
58 mkdir("grindir");
59 if (chdir("grindir") != 0) {
60 printf("grind: chdir grindir failed\n");
61 exit(1);
62 }
63 chdir("/");
65 while (1) {
67 if ((iters % 500) == 0)
68 write(1, which_child ? "B" : "A", 1);
69 int what = rand() % 23;
70 if (what == 1) {
71 close(open("grindir/../a", O_CREATE | O_RDWR));
72 } else if (what == 2) {
73 close(open("grindir/../grindir/../b", O_CREATE | O_RDWR));
74 } else if (what == 3) {
75 unlink("grindir/../a");
76 } else if (what == 4) {
77 if (chdir("grindir") != 0) {
78 printf("grind: chdir grindir failed\n");
79 exit(1);
80 }
81 unlink("../b");
82 chdir("/");
83 } else if (what == 5) {
85 fd = open("/grindir/../a", O_CREATE | O_RDWR);
86 } else if (what == 6) {
88 fd = open("/./grindir/./../b", O_CREATE | O_RDWR);
89 } else if (what == 7) {
90 write(fd, buf, sizeof(buf));
91 } else if (what == 8) {
92 read(fd, buf, sizeof(buf));
93 } else if (what == 9) {
94 mkdir("grindir/../a");
95 close(open("a/../a/./a", O_CREATE | O_RDWR));
96 unlink("a/a");
97 } else if (what == 10) {
98 mkdir("/../b");
99 close(open("grindir/../b/b", O_CREATE | O_RDWR));
100 unlink("b/b");
101 } else if (what == 11) {
102 unlink("b");
103 link("../grindir/./../a", "../b");
104 } else if (what == 12) {
105 unlink("../grindir/../a");
106 link(".././b", "/grindir/../a");
107 } else if (what == 13) {
108 int pid = fork();
109 if (pid == 0) {
110 exit(0);
111 } else if (pid < 0) {
112 printf("grind: fork failed\n");
113 exit(1);
114 }
115 wait(0);
116 } else if (what == 14) {
117 int pid = fork();
118 if (pid == 0) {
121 exit(0);
122 } else if (pid < 0) {
123 printf("grind: fork failed\n");
124 exit(1);
125 }
126 wait(0);
127 } else if (what == 15) {
128 sbrk(6011);
129 } else if (what == 16) {
130 if (sbrk(0) > break0)
131 sbrk(-(sbrk(0) - break0));
132 } else if (what == 17) {
133 int pid = fork();
134 if (pid == 0) {
136 exit(0);
137 } else if (pid < 0) {
138 printf("grind: fork failed\n");
139 exit(1);
140 }
141 if (chdir("../grindir/..") != 0) {
142 printf("grind: chdir failed\n");
143 exit(1);
144 }
146 wait(0);
147 } else if (what == 18) {
148 int pid = fork();
149 if (pid == 0) {
151 exit(0);
152 } else if (pid < 0) {
153 printf("grind: fork failed\n");
154 exit(1);
155 }
156 wait(0);
157 } else if (what == 19) {
158 int fds[2];
159 if (pipe(fds) < 0) {
160 printf("grind: pipe failed\n");
161 exit(1);
162 }
163 int pid = fork();
164 if (pid == 0) {
167 if (write(fds[1], "x", 1) != 1)
168 printf("grind: pipe write failed\n");
169 char c;
170 if (read(fds[0], &c, 1) != 1)
171 printf("grind: pipe read failed\n");
172 exit(0);
173 } else if (pid < 0) {
174 printf("grind: fork failed\n");
175 exit(1);
176 }
179 wait(0);
180 } else if (what == 20) {
181 int pid = fork();
182 if (pid == 0) {
183 unlink("a");
184 mkdir("a");
185 chdir("a");
186 unlink("../a");
188 unlink("x");
189 exit(0);
190 } else if (pid < 0) {
191 printf("grind: fork failed\n");
192 exit(1);
193 }
194 wait(0);
195 } else if (what == 21) {
196 unlink("c");
197 // should always succeed. check that there are free i-nodes,
198 // file descriptors, blocks.
199 int fd1 = open("c", O_CREATE | O_RDWR);
200 if (fd1 < 0) {
201 printf("grind: create c failed\n");
202 exit(1);
203 }
204 if (write(fd1, "x", 1) != 1) {
205 printf("grind: write c failed\n");
206 exit(1);
207 }
208 struct stat st;
209 if (fstat(fd1, &st) != 0) {
210 printf("grind: fstat failed\n");
211 exit(1);
212 }
213 if (st.size != 1) {
214 printf("grind: fstat reports wrong size %d\n", (int)st.size);
215 exit(1);
216 }
217 if (st.ino > 200) {
218 printf("grind: fstat reports crazy i-number %d\n", st.ino);
219 exit(1);
220 }
222 unlink("c");
223 } else if (what == 22) {
224 // echo hi | cat
225 int aa[2], bb[2];
226 if (pipe(aa) < 0) {
227 fprintf(2, "grind: pipe failed\n");
228 exit(1);
229 }
230 if (pipe(bb) < 0) {
231 fprintf(2, "grind: pipe failed\n");
232 exit(1);
233 }
234 int pid1 = fork();
235 if (pid1 == 0) {
236 close(bb[0]);
237 close(bb[1]);
238 close(aa[0]);
240 if (dup(aa[1]) != 1) {
241 fprintf(2, "grind: dup failed\n");
242 exit(1);
243 }
244 close(aa[1]);
245 char *args[3] = {"echo", "hi", 0};
246 exec("grindir/../echo", args);
247 fprintf(2, "grind: echo: not found\n");
248 exit(2);
249 } else if (pid1 < 0) {
250 fprintf(2, "grind: fork failed\n");
251 exit(3);
252 }
253 int pid2 = fork();
254 if (pid2 == 0) {
255 close(aa[1]);
256 close(bb[0]);
258 if (dup(aa[0]) != 0) {
259 fprintf(2, "grind: dup failed\n");
260 exit(4);
261 }
262 close(aa[0]);
264 if (dup(bb[1]) != 1) {
265 fprintf(2, "grind: dup failed\n");
266 exit(5);
267 }
268 close(bb[1]);
269 char *args[2] = {"cat", 0};
270 exec("/cat", args);
271 fprintf(2, "grind: cat: not found\n");
272 exit(6);
273 } else if (pid2 < 0) {
274 fprintf(2, "grind: fork failed\n");
275 exit(7);
276 }
277 close(aa[0]);
278 close(aa[1]);
279 close(bb[1]);
280 char buf[4] = {0, 0, 0, 0};
281 read(bb[0], buf + 0, 1);
282 read(bb[0], buf + 1, 1);
283 read(bb[0], buf + 2, 1);
284 close(bb[0]);
285 int st1, st2;
288 if (st1 != 0 || st2 != 0 || strcmp(buf, "hi\n") != 0) {
289 printf("grind: exec pipeline failed %d %d \"%s\"\n", st1, st2, buf);
290 exit(1);
291 }
292 }
293 }
296void
299 unlink("a");
300 unlink("b");
302 int pid1 = fork();
303 if (pid1 < 0) {
304 printf("grind: fork failed\n");
305 exit(1);
306 }
307 if (pid1 == 0) {
308 rand_next ^= 31;
309 go(0);
310 exit(0);
311 }
313 int pid2 = fork();
314 if (pid2 < 0) {
315 printf("grind: fork failed\n");
316 exit(1);
317 }
318 if (pid2 == 0) {
319 rand_next ^= 7177;
320 go(1);
321 exit(0);
322 }
324 int st1 = -1;
326 if (st1 != 0) {
329 }
330 int st2 = -1;
333 exit(0);
336int
339 while (1) {
340 int pid = fork();
341 if (pid == 0) {
343 exit(0);
344 }
345 if (pid > 0) {
346 wait(0);
347 }
348 pause(20);
350 }