From 3d6ce9b308399f8c49c13653bd4ac21ca2311f26 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Thu, 25 Aug 2022 09:45:35 -0400 Subject: [PATCH] Separate tests in slow and quick. The slow tests run xv6 out of memory, out of disk space, or test big directories. Support -q option to run only the quick tests, which saves about 7mins. Clean up driver by removing duplicated code. --- user/usertests.c | 529 +++++++++++++++++++++++++---------------------- 1 file changed, 278 insertions(+), 251 deletions(-) diff --git a/user/usertests.c b/user/usertests.c index 4f183a5..7d3e9bc 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -21,6 +21,12 @@ char buf[BUFSZ]; +// +// Section with tests that run fairly quickly. Use -q if you want to +// run just those. With -q usertests also runs the ones that take a +// fair of time. +// + // what if you pass ridiculous pointers to system calls // that read user memory with copyin? void @@ -1512,46 +1518,6 @@ linkunlink(char *s) exit(0); } -// directory that uses indirect blocks -void -bigdir(char *s) -{ - enum { N = 500 }; - int i, fd; - char name[10]; - - unlink("bd"); - - fd = open("bd", O_CREATE); - if(fd < 0){ - printf("%s: bigdir create failed\n", s); - exit(1); - } - close(fd); - - for(i = 0; i < N; i++){ - name[0] = 'x'; - name[1] = '0' + (i / 64); - name[2] = '0' + (i % 64); - name[3] = '\0'; - if(link("bd", name) != 0){ - printf("%s: bigdir link(bd, %s) failed\n", s, name); - exit(1); - } - } - - unlink("bd"); - for(i = 0; i < N; i++){ - name[0] = 'x'; - name[1] = '0' + (i / 64); - name[2] = '0' + (i % 64); - name[3] = '\0'; - if(unlink(name) != 0){ - printf("%s: bigdir unlink failed", s); - exit(1); - } - } -} void subdir(char *s) @@ -1758,59 +1724,6 @@ bigwrite(char *s) } } -// concurrent writes to try to provoke deadlock in the virtio disk -// driver. -void -manywrites(char *s) -{ - int nchildren = 4; - int howmany = 30; // increase to look for deadlock - - for(int ci = 0; ci < nchildren; ci++){ - int pid = fork(); - if(pid < 0){ - printf("fork failed\n"); - exit(1); - } - - if(pid == 0){ - char name[3]; - name[0] = 'b'; - name[1] = 'a' + ci; - name[2] = '\0'; - unlink(name); - - for(int iters = 0; iters < howmany; iters++){ - for(int i = 0; i < ci+1; i++){ - int fd = open(name, O_CREATE | O_RDWR); - if(fd < 0){ - printf("%s: cannot create %s\n", s, name); - exit(1); - } - int sz = sizeof(buf); - int cc = write(fd, buf, sz); - if(cc != sz){ - printf("%s: write(%d) ret %d\n", s, sz, cc); - exit(1); - } - close(fd); - } - unlink(name); - } - - unlink(name); - exit(0); - } - } - - for(int ci = 0; ci < nchildren; ci++){ - int st = 0; - wait(&st); - if(st != 0) - exit(st); - } - exit(0); -} void bigfile(char *s) @@ -2642,6 +2555,189 @@ sbrk8000(char *s) } + +// regression test. test whether exec() leaks memory if one of the +// arguments is invalid. the test passes if the kernel doesn't panic. +void +badarg(char *s) +{ + for(int i = 0; i < 50000; i++){ + char *argv[2]; + argv[0] = (char*)0xffffffff; + argv[1] = 0; + exec("echo", argv); + } + + exit(0); +} + +struct test { + void (*f)(char *); + char *s; +} quicktests[] = { + {copyin, "copyin"}, + {copyout, "copyout"}, + {copyinstr1, "copyinstr1"}, + {copyinstr2, "copyinstr2"}, + {copyinstr3, "copyinstr3"}, + {rwsbrk, "rwsbrk" }, + {truncate1, "truncate1"}, + {truncate2, "truncate2"}, + {truncate3, "truncate3"}, + {openiputtest, "openiput"}, + {exitiputtest, "exitiput"}, + {iputtest, "iput"}, + {opentest, "opentest"}, + {writetest, "writetest"}, + {writebig, "writebig"}, + {createtest, "createtest"}, + {dirtest, "dirtest"}, + {exectest, "exectest"}, + {pipe1, "pipe1"}, + {killstatus, "killstatus"}, + {preempt, "preempt"}, + {exitwait, "exitwait"}, + {reparent, "reparent" }, + {twochildren, "twochildren"}, + {forkfork, "forkfork"}, + {forkforkfork, "forkforkfork"}, + {reparent2, "reparent2"}, + {mem, "mem"}, + {sharedfd, "sharedfd"}, + {fourfiles, "fourfiles"}, + {createdelete, "createdelete"}, + {unlinkread, "unlinkread"}, + {linktest, "linktest"}, + {concreate, "concreate"}, + {linkunlink, "linkunlink"}, + {subdir, "subdir"}, + {bigwrite, "bigwrite"}, + {bigfile, "bigfile"}, + {fourteen, "fourteen"}, + {rmdot, "rmdot"}, + {dirfile, "dirfile"}, + {iref, "iref"}, + {forktest, "forktest"}, + {sbrkbasic, "sbrkbasic"}, + {sbrkmuch, "sbrkmuch"}, + {kernmem, "kernmem"}, + {MAXVAplus, "MAXVAplus"}, + {sbrkfail, "sbrkfail"}, + {sbrkarg, "sbrkarg"}, + {validatetest, "validatetest"}, + {bsstest, "bsstest"}, + {bigargtest, "bigargtest"}, + {argptest, "argptest"}, + {stacktest, "stacktest"}, + {textwrite, "textwrite"}, + {pgbug, "pgbug" }, + {sbrkbugs, "sbrkbugs" }, + {sbrklast, "sbrklast"}, + {sbrk8000, "sbrk8000"}, + {badarg, "badarg" }, + + { 0, 0}, +}; + +// +// Section with tests that take a fair bit of time +// + +// directory that uses indirect blocks +void +bigdir(char *s) +{ + enum { N = 500 }; + int i, fd; + char name[10]; + + unlink("bd"); + + fd = open("bd", O_CREATE); + if(fd < 0){ + printf("%s: bigdir create failed\n", s); + exit(1); + } + close(fd); + + for(i = 0; i < N; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(link("bd", name) != 0){ + printf("%s: bigdir link(bd, %s) failed\n", s, name); + exit(1); + } + } + + unlink("bd"); + for(i = 0; i < N; i++){ + name[0] = 'x'; + name[1] = '0' + (i / 64); + name[2] = '0' + (i % 64); + name[3] = '\0'; + if(unlink(name) != 0){ + printf("%s: bigdir unlink failed", s); + exit(1); + } + } +} + +// concurrent writes to try to provoke deadlock in the virtio disk +// driver. +void +manywrites(char *s) +{ + int nchildren = 4; + int howmany = 30; // increase to look for deadlock + + for(int ci = 0; ci < nchildren; ci++){ + int pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(1); + } + + if(pid == 0){ + char name[3]; + name[0] = 'b'; + name[1] = 'a' + ci; + name[2] = '\0'; + unlink(name); + + for(int iters = 0; iters < howmany; iters++){ + for(int i = 0; i < ci+1; i++){ + int fd = open(name, O_CREATE | O_RDWR); + if(fd < 0){ + printf("%s: cannot create %s\n", s, name); + exit(1); + } + int sz = sizeof(buf); + int cc = write(fd, buf, sz); + if(cc != sz){ + printf("%s: write(%d) ret %d\n", s, sz, cc); + exit(1); + } + close(fd); + } + unlink(name); + } + + unlink(name); + exit(0); + } + } + + for(int ci = 0; ci < nchildren; ci++){ + int st = 0; + wait(&st); + if(st != 0) + exit(st); + } + exit(0); +} + // regression test. does write() with an invalid buffer pointer cause // a block to be allocated for a file that is then not freed when the // file is deleted? if the kernel has this bug, it will panic: balloc: @@ -2679,21 +2775,6 @@ badwrite(char *s) exit(0); } -// regression test. test whether exec() leaks memory if one of the -// arguments is invalid. the test passes if the kernel doesn't panic. -void -badarg(char *s) -{ - for(int i = 0; i < 50000; i++){ - char *argv[2]; - argv[0] = (char*)0xffffffff; - argv[1] = 0; - exec("echo", argv); - } - - exit(0); -} - // test the exec() code that cleans up if it runs out // of memory. it's really a test that such a condition // doesn't cause a panic. @@ -2843,6 +2924,60 @@ outofinodes(char *s) } } +struct test slowtests[] = { + {bigdir, "bigdir"}, + {manywrites, "manywrites"}, + {badwrite, "badwrite" }, + {execout, "execout"}, + {diskfull, "diskfull"}, + {outofinodes, "outofinodes"}, + + { 0, 0}, +}; + +// +// drive tests +// + +// run each test in its own process. run returns 1 if child's exit() +// indicates success. +int +run(void f(char *), char *s) { + int pid; + int xstatus; + + printf("test %s: ", s); + if((pid = fork()) < 0) { + printf("runtest: fork error\n"); + exit(1); + } + if(pid == 0) { + f(s); + exit(0); + } else { + wait(&xstatus); + if(xstatus != 0) + printf("FAILED\n"); + else + printf("OK\n"); + return xstatus == 0; + } +} + +int +runtests(struct test *tests, char *justone) { + for (struct test *t = tests; t->s != 0; t++) { + if((justone == 0) || strcmp(t->s, justone) == 0) { + if(!run(t->f, t->s)){ + printf("SOME TESTS FAILED\n"); + return 1; + } + } + } + return 0; +} + + // // use sbrk() to count how many free physical memory pages there are. // touches the pages to force allocation. @@ -2909,166 +3044,58 @@ countfree() return n; } -// run each test in its own process. run returns 1 if child's exit() -// indicates success. int -run(void f(char *), char *s) { - int pid; - int xstatus; - - printf("test %s: ", s); - if((pid = fork()) < 0) { - printf("runtest: fork error\n"); - exit(1); - } - if(pid == 0) { - f(s); - exit(0); - } else { - wait(&xstatus); - if(xstatus != 0) - printf("FAILED\n"); - else - printf("OK\n"); - return xstatus == 0; - } +drivetests(int quick, int continuous, char *justone) { + do { + printf("usertests starting\n"); + int free0 = countfree(); + int free1 = 0; + if (runtests(quicktests, justone)) { + if(continuous != 2) { + return 1; + } + } + if(!quick) { + if (justone == 0) + printf("usertests slow tests starting\n"); + if (runtests(slowtests, justone)) { + if(continuous != 2) { + return 1; + } + } + } + if((free1 = countfree()) < free0) { + printf("FAILED -- lost some free pages %d (out of %d)\n", free1, free0); + if(continuous != 2) { + return 1; + } + } + } while(continuous); + return 0; } int main(int argc, char *argv[]) { int continuous = 0; + int quick = 0; char *justone = 0; - if(argc == 2 && strcmp(argv[1], "-c") == 0){ + if(argc == 2 && strcmp(argv[1], "-q") == 0){ + quick = 1; + } else if(argc == 2 && strcmp(argv[1], "-c") == 0){ continuous = 1; } else if(argc == 2 && strcmp(argv[1], "-C") == 0){ continuous = 2; } else if(argc == 2 && argv[1][0] != '-'){ justone = argv[1]; } else if(argc > 1){ - printf("Usage: usertests [-c] [testname]\n"); + printf("Usage: usertests [-c] [-C] [-q] [testname]\n"); exit(1); } - - struct test { - void (*f)(char *); - char *s; - } tests[] = { - {copyin, "copyin"}, - {copyout, "copyout"}, - {copyinstr1, "copyinstr1"}, - {copyinstr2, "copyinstr2"}, - {copyinstr3, "copyinstr3"}, - {rwsbrk, "rwsbrk" }, - {truncate1, "truncate1"}, - {truncate2, "truncate2"}, - {truncate3, "truncate3"}, - {openiputtest, "openiput"}, - {exitiputtest, "exitiput"}, - {iputtest, "iput"}, - {opentest, "opentest"}, - {writetest, "writetest"}, - {writebig, "writebig"}, - {createtest, "createtest"}, - {dirtest, "dirtest"}, - {exectest, "exectest"}, - {pipe1, "pipe1"}, - {killstatus, "killstatus"}, - {preempt, "preempt"}, - {exitwait, "exitwait"}, - {reparent, "reparent" }, - {twochildren, "twochildren"}, - {forkfork, "forkfork"}, - {forkforkfork, "forkforkfork"}, - {reparent2, "reparent2"}, - {mem, "mem"}, - {sharedfd, "sharedfd"}, - {fourfiles, "fourfiles"}, - {createdelete, "createdelete"}, - {unlinkread, "unlinkread"}, - {linktest, "linktest"}, - {concreate, "concreate"}, - {linkunlink, "linkunlink"}, - {bigdir, "bigdir"}, // slow - {subdir, "subdir"}, - {bigwrite, "bigwrite"}, - {manywrites, "manywrites"}, - {bigfile, "bigfile"}, - {fourteen, "fourteen"}, - {rmdot, "rmdot"}, - {dirfile, "dirfile"}, - {iref, "iref"}, - {forktest, "forktest"}, - {sbrkbasic, "sbrkbasic"}, - {sbrkmuch, "sbrkmuch"}, - {kernmem, "kernmem"}, - {MAXVAplus, "MAXVAplus"}, - {sbrkfail, "sbrkfail"}, - {sbrkarg, "sbrkarg"}, - {validatetest, "validatetest"}, - {bsstest, "bsstest"}, - {bigargtest, "bigargtest"}, - {argptest, "argptest"}, - {stacktest, "stacktest"}, - {textwrite, "textwrite"}, - {pgbug, "pgbug" }, - {sbrkbugs, "sbrkbugs" }, - {sbrklast, "sbrklast"}, - {sbrk8000, "sbrk8000"}, - {badwrite, "badwrite" }, - {badarg, "badarg" }, - {execout, "execout"}, - {diskfull, "diskfull"}, - {outofinodes, "outofinodes"}, - - { 0, 0}, - }; - - if(continuous){ - printf("continuous usertests starting\n"); - while(1){ - int fail = 0; - int free0 = countfree(); - for (struct test *t = tests; t->s != 0; t++) { - if(!run(t->f, t->s)){ - fail = 1; - break; - } - } - if(fail){ - printf("SOME TESTS FAILED\n"); - if(continuous != 2) - exit(1); - } - int free1 = countfree(); - if(free1 < free0){ - printf("FAILED -- lost %d free pages\n", free0 - free1); - if(continuous != 2) - exit(1); - } - } - } - - printf("usertests starting\n"); - int free0 = countfree(); - int free1 = 0; - int fail = 0; - for (struct test *t = tests; t->s != 0; t++) { - if((justone == 0) || strcmp(t->s, justone) == 0) { - if(!run(t->f, t->s)) - fail = 1; - } - } - - if(fail){ - printf("SOME TESTS FAILED\n"); + if (drivetests(quick, continuous, justone)) { exit(1); - } else if((free1 = countfree()) < free0){ - printf("FAILED -- lost some free pages %d (out of %d)\n", free1, free0); - exit(1); - } else { - printf("ALL TESTS PASSED\n"); - exit(0); } + printf("ALL TESTS PASSED\n"); + exit(0); }