diff --git a/automation_testing b/automation_testing new file mode 100644 index 0000000..bd9525e --- /dev/null +++ b/automation_testing @@ -0,0 +1,509 @@ +diff --git a/kern/conf/conf.kern b/kern/conf/conf.kern +index 2a29022..3fa5742 100644 +--- a/kern/conf/conf.kern ++++ b/kern/conf/conf.kern +@@ -461,3 +461,6 @@ defoption synchprobs + optfile synchprobs synchprobs/whalemating.c + optfile synchprobs synchprobs/stoplight.c + optfile synchprobs test/synchprobs.c ++ ++defoption automationtest ++optfile automationtest test/automationtest.c +diff --git a/kern/include/lib.h b/kern/include/lib.h +index d1ae75d..a5aca71 100644 +--- a/kern/include/lib.h ++++ b/kern/include/lib.h +@@ -201,9 +201,10 @@ void random_spinner(uint32_t); + /* + * Testing variants of kprintf. tprintf is silent during automated testing. + * sprintf prefixes the kernel secret to kprintf messages during automated +- * testing. ++ * testing. nprintf is not silent during automated testing. + */ + + int tkprintf(const char *format, ...) __PF(1,2); ++int nkprintf(const char *format, ...) __PF(1,2); + + #endif /* _LIB_H_ */ +diff --git a/kern/include/test.h b/kern/include/test.h +index a2458a2..260e515 100644 +--- a/kern/include/test.h ++++ b/kern/include/test.h +@@ -31,6 +31,7 @@ + #define _TEST_H_ + + #include "opt-synchprobs.h" ++#include "opt-automationtest.h" + + /* + * Declarations for test code and other miscellaneous high-level +@@ -133,4 +134,14 @@ int stoplight(int, char **); + + #endif + ++/* ++ * Automation tests for detecting kernel deadlocks and livelocks. ++ */ ++ ++#if OPT_AUTOMATIONTEST ++int dltest(int, char **); ++int ll1test(int, char **); ++int ll16test(int, char **); ++#endif ++ + #endif /* _TEST_H_ */ +diff --git a/kern/lib/kprintf.c b/kern/lib/kprintf.c +index c8a3baa..0a6fe90 100644 +--- a/kern/lib/kprintf.c ++++ b/kern/lib/kprintf.c +@@ -159,6 +159,25 @@ tkprintf(const char *fmt, ...) + + return chars; + } ++/* ++ * kprintf variant that is quiet during non-automated testing ++ */ ++int ++nkprintf(const char *fmt, ...) ++{ ++ int chars; ++ va_list ap; ++ ++ if (strcmp(KERNEL_SECRET, "") == 0) { ++ return 0; ++ } ++ ++ va_start(ap, fmt); ++ chars = vkprintf(fmt, ap); ++ va_end(ap); ++ ++ return chars; ++} + + /* + * panic() is for fatal errors. It prints the printf arguments it's +diff --git a/kern/main/menu.c b/kern/main/menu.c +index d0ed255..abb99cb 100644 +--- a/kern/main/menu.c ++++ b/kern/main/menu.c +@@ -48,6 +48,7 @@ + #include "opt-sfs.h" + #include "opt-net.h" + #include "opt-synchprobs.h" ++#include "opt-automationtest.h" + + /* + * In-kernel menu and command dispatcher. +@@ -530,6 +531,29 @@ cmd_testmenu(int n, char **a) + return 0; + } + ++#if OPT_AUTOMATIONTEST ++static const char *automationmenu[] = { ++ "[dl] Deadlock test (*) ", ++ "[ll1] Livelock test (1 thread) ", ++ "[ll16] Livelock test (16 threads) ", ++ NULL ++}; ++ ++static ++int ++cmd_automationmenu(int n, char **a) ++{ ++ (void)n; ++ (void)a; ++ ++ showmenu("OS/161 automation tests menu", automationmenu); ++ kprintf(" (*) These tests require locks.\n"); ++ kprintf("\n"); ++ ++ return 0; ++} ++#endif ++ + static const char *mainmenu[] = { + "[?o] Operations menu ", + "[?t] Tests menu ", +@@ -566,6 +590,9 @@ static struct { + { "help", cmd_mainmenu }, + { "?o", cmd_opsmenu }, + { "?t", cmd_testmenu }, ++#if OPT_AUTOMATIONTEST ++ { "?a", cmd_automationmenu }, ++#endif + + /* operations */ + { "s", cmd_shell }, +@@ -654,6 +681,13 @@ static struct { + { "fs4", writestress2 }, + { "fs5", longstress }, + { "fs6", createstress }, ++ ++#if OPT_AUTOMATIONTEST ++ /* automation tests */ ++ { "dl", dltest }, ++ { "ll1", ll1test }, ++ { "ll16", ll16test }, ++#endif + + { NULL, NULL } + }; +diff --git a/kern/test/automationtest.c b/kern/test/automationtest.c +new file mode 100644 +index 0000000..2dce056 +--- /dev/null ++++ b/kern/test/automationtest.c +@@ -0,0 +1,175 @@ ++/* ++ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009 ++ * The President and Fellows of Harvard College. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ ++ ++/* ++ * Automation test code for creating (and detecting) kernel dead and livelocks. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MAX_SPINNERS 16 ++ ++static struct lock *deadlock_locks[2]; ++static struct semaphore *deadlock_sem; ++ ++struct spinlock spinners_lock[MAX_SPINNERS]; ++ ++static ++void ++inititems(void) ++{ ++ int i; ++ ++ for (i = 0; i < 2; i++) { ++ deadlock_locks[i] = lock_create("deadlock lock"); ++ if (deadlock_locks[i] == NULL) { ++ panic("automationtest: lock_create failed\n"); ++ } ++ } ++ deadlock_sem = sem_create("deadlock sem", 0); ++ if (deadlock_sem == NULL) { ++ panic("automationtest: sem_create failed\n"); ++ } ++ ++ for (i = 0; i < MAX_SPINNERS; i++) { ++ spinlock_init(&(spinners_lock[i])); ++ } ++} ++ ++static ++void ++dltestthread(void *junk1, unsigned long junk2) ++{ ++ (void)junk1; ++ (void)junk2; ++ ++ lock_acquire(deadlock_locks[1]); ++ V(deadlock_sem); ++ lock_acquire(deadlock_locks[0]); ++} ++ ++int ++dltest(int nargs, char **args) ++{ ++ int result; ++ ++ (void)nargs; ++ (void)args; ++ ++ inititems(); ++ ++ lock_acquire(deadlock_locks[0]); ++ ++ result = thread_fork("dltest", NULL, dltestthread, NULL, (unsigned long)0); ++ if (result) { ++ panic("dltest: thread_fork failed: %s\n", strerror(result)); ++ } ++ ++ P(deadlock_sem); ++ lock_acquire(deadlock_locks[1]); ++ ++ panic("dltest: didn't create deadlock (locks probably don't work)\n"); ++ ++ // 09 Jan 2015 : GWA : Shouldn't return. ++ return 0; ++} ++ ++inline ++static ++void ++infinite_spinner(unsigned long i) ++{ ++ (void)i; ++ volatile int j; ++ ++ for (j=0; j<10000000; j++); ++ ++ spinlock_acquire(&(spinners_lock[i])); ++ ++ for (j=0; j<=1000; j++) { ++ if (j == 1000) { ++ j = 0; ++ } ++ } ++ panic("ll1test: infinite spin loop completed\n"); ++} ++ ++int ++ll1test(int nargs, char **args) ++{ ++ (void)nargs; ++ (void)args; ++ ++ inititems(); ++ ++ infinite_spinner((unsigned long) 0); ++ ++ // 09 Jan 2015 : GWA : Shouldn't return. ++ return 0; ++} ++ ++ ++static ++void ++ll16testthread(void *junk1, unsigned long i) ++{ ++ (void)junk1; ++ ++ infinite_spinner(i); ++} ++ ++int ++ll16test(int nargs, char **args) ++{ ++ int i, result; ++ ++ inititems(); ++ ++ (void)nargs; ++ (void)args; ++ ++ for (i=1; i<16; i++) { ++ result = thread_fork("ll16testthread", NULL, ll16testthread, NULL, (unsigned long)i); ++ if (result) { ++ panic("ll16test: thread_fork failed: %s\n", strerror(result)); ++ } ++ } ++ infinite_spinner(0); ++ ++ // 09 Jan 2015 : GWA : Shouldn't return. ++ return 0; ++} +diff --git a/userland/include/stdio.h b/userland/include/stdio.h +index 440a822..772277f 100644 +--- a/userland/include/stdio.h ++++ b/userland/include/stdio.h +@@ -59,6 +59,7 @@ int vsnprintf(char *buf, size_t len, const char *fmt, __va_list ap); + /* Automated testing extensions. */ + + int tprintf(const char *fmt, ...); ++int nprintf(const char *fmt, ...); + int printsf(const char *fmt, ...); + + /* Print the argument string and then a newline. Returns 0 or -1 on error. */ +diff --git a/userland/lib/libc/stdio/printf.c b/userland/lib/libc/stdio/printf.c +index 52a8161..30d923d 100644 +--- a/userland/lib/libc/stdio/printf.c ++++ b/userland/lib/libc/stdio/printf.c +@@ -97,6 +97,24 @@ tprintf(const char *fmt, ...) + return chars; + } + ++/* printf variant that is loud during automated testing */ ++int ++nprintf(const char *fmt, ...) ++{ ++ int chars; ++ va_list ap; ++ ++ if (strcmp(KERNEL_SECRET, "") == 0) { ++ return 0; ++ } ++ ++ va_start(ap, fmt); ++ chars = vprintf(fmt, ap); ++ va_end(ap); ++ ++ return chars; ++} ++ + /* printf variant that prepends the kernel secret */ + int + printsf(const char *fmt, ...) +diff --git a/userland/testbin/Makefile b/userland/testbin/Makefile +index e5cf502..3e16de0 100644 +--- a/userland/testbin/Makefile ++++ b/userland/testbin/Makefile +@@ -10,8 +10,8 @@ SUBDIRS=add argtest badcall bigexec bigfile bigfork bigseek bloat conman \ + filetest forkbomb forktest frack guzzle hash hog huge kitchen \ + malloctest matmult multiexec palin parallelvm poisondisk psort \ + quinthuge quintmat quintsort randcall redirect rmdirtest rmtest \ +- sbrktest schedpong sink sort sparsefile sty tail tictac triplehuge \ +- triplemat triplesort usemtest zero ++ sbrktest schedpong sink sort sparsefile spinner sty tail tictac \ ++ triplehuge triplemat triplesort usemtest waiter zero + + # But not: + # userthreads (no support in kernel API in base system) +diff --git a/userland/testbin/spinner/Makefile b/userland/testbin/spinner/Makefile +new file mode 100644 +index 0000000..2eaac72 +--- /dev/null ++++ b/userland/testbin/spinner/Makefile +@@ -0,0 +1,11 @@ ++# Makefile for spinner ++ ++TOP=../../.. ++.include "$(TOP)/mk/os161.config.mk" ++ ++PROG=spinner ++SRCS=spinner.c ++BINDIR=/testbin ++ ++.include "$(TOP)/mk/os161.prog.mk" ++ +diff --git a/userland/testbin/spinner/spinner.c b/userland/testbin/spinner/spinner.c +new file mode 100644 +index 0000000..cffb3aa +--- /dev/null ++++ b/userland/testbin/spinner/spinner.c +@@ -0,0 +1,47 @@ ++/* ++ * spinner.c ++ * ++ * Spins as hard as it can, forking multiple processes as needed. Intended to ++ * test our ability to detect stuck processes in userspace. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++static ++void ++spin(void) ++{ ++ volatile int i; ++ ++ for (i=0; i <= 1000; i++) { ++ if (i == 1000) { ++ i = 0; ++ } ++ } ++} ++ ++int ++main(int argc, char **argv) ++{ ++ int i, count, pid; ++ ++ if (argc != 2) { ++ errx(1, "Usage: spinner "); ++ } ++ count = atoi(argv[1]); ++ ++ for (i = 1; i < count; i++) { ++ pid = fork(); ++ if (pid != 0) { ++ spin(); ++ } ++ } ++ spin(); ++ errx(2, "spinner: spin returned"); ++ ++ // 09 Jan 2015 : GWA : Shouldn't get here. ++ return 0; ++} +diff --git a/userland/testbin/waiter/Makefile b/userland/testbin/waiter/Makefile +new file mode 100644 +index 0000000..fdad038 +--- /dev/null ++++ b/userland/testbin/waiter/Makefile +@@ -0,0 +1,11 @@ ++# Makefile for waiter ++ ++TOP=../../.. ++.include "$(TOP)/mk/os161.config.mk" ++ ++PROG=waiter ++SRCS=waiter.c ++BINDIR=/testbin ++ ++.include "$(TOP)/mk/os161.prog.mk" ++ +diff --git a/userland/testbin/waiter/waiter.c b/userland/testbin/waiter/waiter.c +new file mode 100644 +index 0000000..575d631 +--- /dev/null ++++ b/userland/testbin/waiter/waiter.c +@@ -0,0 +1,29 @@ ++/* ++ * waiter.c ++ * ++ * Just sits there without doing anything. We use the read system call just to ++ * provide a way to wait. Intended to test our ability to detect stuck ++ * processes in userspace. ++ */ ++ ++#include ++#include ++ ++int ++main(void) ++{ ++ char ch=0; ++ int len; ++ ++ while (ch!='q') { ++ len = read(STDIN_FILENO, &ch, 1); ++ if (len < 0) { ++ err(1, "stdin: read"); ++ } ++ if (len==0) { ++ /* EOF */ ++ break; ++ } ++ } ++ return 0; ++} diff --git a/kern/conf/conf.kern b/kern/conf/conf.kern index 093b676..2fbfc0f 100644 --- a/kern/conf/conf.kern +++ b/kern/conf/conf.kern @@ -443,3 +443,6 @@ defoption synchprobs optfile synchprobs synchprobs/whalemating.c optfile synchprobs synchprobs/stoplight.c optfile synchprobs test/synchprobs.c + +defoption automationtest +optfile automationtest test/automationtest.c diff --git a/kern/include/lib.h b/kern/include/lib.h index b114e58..fe8cb6d 100644 --- a/kern/include/lib.h +++ b/kern/include/lib.h @@ -200,9 +200,10 @@ void random_spinner(uint32_t); /* * Testing variants of kprintf. tprintf is silent during automated testing. * sprintf prefixes the kernel secret to kprintf messages during automated - * testing. + * testing. nprintf is not silent during automated testing. */ int tkprintf(const char *format, ...) __PF(1,2); +int nkprintf(const char *format, ...) __PF(1,2); #endif /* _LIB_H_ */ diff --git a/kern/include/test.h b/kern/include/test.h index 049ace6..7b8c17a 100644 --- a/kern/include/test.h +++ b/kern/include/test.h @@ -31,6 +31,7 @@ #define _TEST_H_ #include "opt-synchprobs.h" +#include "opt-automationtest.h" /* * Declarations for test code and other miscellaneous high-level @@ -127,4 +128,14 @@ int stoplight(int, char **); #endif +/* + * Automation tests for detecting kernel deadlocks and livelocks. + */ + +#if OPT_AUTOMATIONTEST +int dltest(int, char **); +int ll1test(int, char **); +int ll16test(int, char **); +#endif + #endif /* _TEST_H_ */ diff --git a/kern/lib/kprintf.c b/kern/lib/kprintf.c index c8a3baa..0a6fe90 100644 --- a/kern/lib/kprintf.c +++ b/kern/lib/kprintf.c @@ -159,6 +159,25 @@ tkprintf(const char *fmt, ...) return chars; } +/* + * kprintf variant that is quiet during non-automated testing + */ +int +nkprintf(const char *fmt, ...) +{ + int chars; + va_list ap; + + if (strcmp(KERNEL_SECRET, "") == 0) { + return 0; + } + + va_start(ap, fmt); + chars = vkprintf(fmt, ap); + va_end(ap); + + return chars; +} /* * panic() is for fatal errors. It prints the printf arguments it's diff --git a/kern/main/menu.c b/kern/main/menu.c index c74e6e0..832ce36 100644 --- a/kern/main/menu.c +++ b/kern/main/menu.c @@ -45,6 +45,7 @@ #include "opt-sfs.h" #include "opt-net.h" #include "opt-synchprobs.h" +#include "opt-automationtest.h" /* * In-kernel menu and command dispatcher. @@ -508,6 +509,29 @@ cmd_testmenu(int n, char **a) return 0; } +#if OPT_AUTOMATIONTEST +static const char *automationmenu[] = { + "[dl] Deadlock test (*) ", + "[ll1] Livelock test (1 thread) ", + "[ll16] Livelock test (16 threads) ", + NULL +}; + +static +int +cmd_automationmenu(int n, char **a) +{ + (void)n; + (void)a; + + showmenu("OS/161 automation tests menu", automationmenu); + kprintf(" (*) These tests require locks.\n"); + kprintf("\n"); + + return 0; +} +#endif + static const char *mainmenu[] = { "[?o] Operations menu ", "[?t] Tests menu ", @@ -543,6 +567,9 @@ static struct { { "help", cmd_mainmenu }, { "?o", cmd_opsmenu }, { "?t", cmd_testmenu }, +#if OPT_AUTOMATIONTEST + { "?a", cmd_automationmenu }, +#endif /* operations */ { "s", cmd_shell }, @@ -622,6 +649,13 @@ static struct { { "fs4", writestress2 }, { "fs5", longstress }, { "fs6", createstress }, + +#if OPT_AUTOMATIONTEST + /* automation tests */ + { "dl", dltest }, + { "ll1", ll1test }, + { "ll16", ll16test }, +#endif { NULL, NULL } }; diff --git a/kern/test/automationtest.c b/kern/test/automationtest.c new file mode 100644 index 0000000..2dce056 --- /dev/null +++ b/kern/test/automationtest.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009 + * The President and Fellows of Harvard College. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Automation test code for creating (and detecting) kernel dead and livelocks. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_SPINNERS 16 + +static struct lock *deadlock_locks[2]; +static struct semaphore *deadlock_sem; + +struct spinlock spinners_lock[MAX_SPINNERS]; + +static +void +inititems(void) +{ + int i; + + for (i = 0; i < 2; i++) { + deadlock_locks[i] = lock_create("deadlock lock"); + if (deadlock_locks[i] == NULL) { + panic("automationtest: lock_create failed\n"); + } + } + deadlock_sem = sem_create("deadlock sem", 0); + if (deadlock_sem == NULL) { + panic("automationtest: sem_create failed\n"); + } + + for (i = 0; i < MAX_SPINNERS; i++) { + spinlock_init(&(spinners_lock[i])); + } +} + +static +void +dltestthread(void *junk1, unsigned long junk2) +{ + (void)junk1; + (void)junk2; + + lock_acquire(deadlock_locks[1]); + V(deadlock_sem); + lock_acquire(deadlock_locks[0]); +} + +int +dltest(int nargs, char **args) +{ + int result; + + (void)nargs; + (void)args; + + inititems(); + + lock_acquire(deadlock_locks[0]); + + result = thread_fork("dltest", NULL, dltestthread, NULL, (unsigned long)0); + if (result) { + panic("dltest: thread_fork failed: %s\n", strerror(result)); + } + + P(deadlock_sem); + lock_acquire(deadlock_locks[1]); + + panic("dltest: didn't create deadlock (locks probably don't work)\n"); + + // 09 Jan 2015 : GWA : Shouldn't return. + return 0; +} + +inline +static +void +infinite_spinner(unsigned long i) +{ + (void)i; + volatile int j; + + for (j=0; j<10000000; j++); + + spinlock_acquire(&(spinners_lock[i])); + + for (j=0; j<=1000; j++) { + if (j == 1000) { + j = 0; + } + } + panic("ll1test: infinite spin loop completed\n"); +} + +int +ll1test(int nargs, char **args) +{ + (void)nargs; + (void)args; + + inititems(); + + infinite_spinner((unsigned long) 0); + + // 09 Jan 2015 : GWA : Shouldn't return. + return 0; +} + + +static +void +ll16testthread(void *junk1, unsigned long i) +{ + (void)junk1; + + infinite_spinner(i); +} + +int +ll16test(int nargs, char **args) +{ + int i, result; + + inititems(); + + (void)nargs; + (void)args; + + for (i=1; i<16; i++) { + result = thread_fork("ll16testthread", NULL, ll16testthread, NULL, (unsigned long)i); + if (result) { + panic("ll16test: thread_fork failed: %s\n", strerror(result)); + } + } + infinite_spinner(0); + + // 09 Jan 2015 : GWA : Shouldn't return. + return 0; +} diff --git a/userland/include/stdio.h b/userland/include/stdio.h index 440a822..772277f 100644 --- a/userland/include/stdio.h +++ b/userland/include/stdio.h @@ -59,6 +59,7 @@ int vsnprintf(char *buf, size_t len, const char *fmt, __va_list ap); /* Automated testing extensions. */ int tprintf(const char *fmt, ...); +int nprintf(const char *fmt, ...); int printsf(const char *fmt, ...); /* Print the argument string and then a newline. Returns 0 or -1 on error. */ diff --git a/userland/lib/libc/stdio/printf.c b/userland/lib/libc/stdio/printf.c index 52a8161..30d923d 100644 --- a/userland/lib/libc/stdio/printf.c +++ b/userland/lib/libc/stdio/printf.c @@ -97,6 +97,24 @@ tprintf(const char *fmt, ...) return chars; } +/* printf variant that is loud during automated testing */ +int +nprintf(const char *fmt, ...) +{ + int chars; + va_list ap; + + if (strcmp(KERNEL_SECRET, "") == 0) { + return 0; + } + + va_start(ap, fmt); + chars = vprintf(fmt, ap); + va_end(ap); + + return chars; +} + /* printf variant that prepends the kernel secret */ int printsf(const char *fmt, ...) diff --git a/userland/testbin/Makefile b/userland/testbin/Makefile index e5cf502..3e16de0 100644 --- a/userland/testbin/Makefile +++ b/userland/testbin/Makefile @@ -10,8 +10,8 @@ SUBDIRS=add argtest badcall bigexec bigfile bigfork bigseek bloat conman \ filetest forkbomb forktest frack guzzle hash hog huge kitchen \ malloctest matmult multiexec palin parallelvm poisondisk psort \ quinthuge quintmat quintsort randcall redirect rmdirtest rmtest \ - sbrktest schedpong sink sort sparsefile sty tail tictac triplehuge \ - triplemat triplesort usemtest zero + sbrktest schedpong sink sort sparsefile spinner sty tail tictac \ + triplehuge triplemat triplesort usemtest waiter zero # But not: # userthreads (no support in kernel API in base system) diff --git a/userland/testbin/spinner/Makefile b/userland/testbin/spinner/Makefile new file mode 100644 index 0000000..2eaac72 --- /dev/null +++ b/userland/testbin/spinner/Makefile @@ -0,0 +1,11 @@ +# Makefile for spinner + +TOP=../../.. +.include "$(TOP)/mk/os161.config.mk" + +PROG=spinner +SRCS=spinner.c +BINDIR=/testbin + +.include "$(TOP)/mk/os161.prog.mk" + diff --git a/userland/testbin/spinner/spinner.c b/userland/testbin/spinner/spinner.c new file mode 100644 index 0000000..cffb3aa --- /dev/null +++ b/userland/testbin/spinner/spinner.c @@ -0,0 +1,47 @@ +/* + * spinner.c + * + * Spins as hard as it can, forking multiple processes as needed. Intended to + * test our ability to detect stuck processes in userspace. + */ + +#include +#include +#include +#include + +static +void +spin(void) +{ + volatile int i; + + for (i=0; i <= 1000; i++) { + if (i == 1000) { + i = 0; + } + } +} + +int +main(int argc, char **argv) +{ + int i, count, pid; + + if (argc != 2) { + errx(1, "Usage: spinner "); + } + count = atoi(argv[1]); + + for (i = 1; i < count; i++) { + pid = fork(); + if (pid != 0) { + spin(); + } + } + spin(); + errx(2, "spinner: spin returned"); + + // 09 Jan 2015 : GWA : Shouldn't get here. + return 0; +} diff --git a/userland/testbin/waiter/Makefile b/userland/testbin/waiter/Makefile new file mode 100644 index 0000000..fdad038 --- /dev/null +++ b/userland/testbin/waiter/Makefile @@ -0,0 +1,11 @@ +# Makefile for waiter + +TOP=../../.. +.include "$(TOP)/mk/os161.config.mk" + +PROG=waiter +SRCS=waiter.c +BINDIR=/testbin + +.include "$(TOP)/mk/os161.prog.mk" + diff --git a/userland/testbin/waiter/waiter.c b/userland/testbin/waiter/waiter.c new file mode 100644 index 0000000..575d631 --- /dev/null +++ b/userland/testbin/waiter/waiter.c @@ -0,0 +1,29 @@ +/* + * waiter.c + * + * Just sits there without doing anything. We use the read system call just to + * provide a way to wait. Intended to test our ability to detect stuck + * processes in userspace. + */ + +#include +#include + +int +main(void) +{ + char ch=0; + int len; + + while (ch!='q') { + len = read(STDIN_FILENO, &ch, 1); + if (len < 0) { + err(1, "stdin: read"); + } + if (len==0) { + /* EOF */ + break; + } + } + return 0; +}