410 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			410 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Tests for getitimer(2)/setitimer(2) - by D.C. van Moolenbroek */
 | |
| /* Warning: this test deals with (real and virtual) time, and, lacking a proper
 | |
|  * point of reference, its correctness depends on circumstances like CPU speed
 | |
|  * and system load. A succeeding test run says a lot - failure not so much. */
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <time.h>
 | |
| #include <minix/config.h>
 | |
| #include <minix/sysinfo.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/time.h>
 | |
| #include <sys/wait.h>
 | |
| #include <signal.h>
 | |
| #include <unistd.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #define ITERATIONS 3
 | |
| #define MAX_ERROR 4
 | |
| 
 | |
| /* we have to keep in mind the millisecond values are rounded up */
 | |
| #define UPPERUSEC(us) ((us)+(1000000/system_hz))
 | |
| #define EQUSEC(l,r) \
 | |
|   ((l) <= ((r) + (1000000/system_hz)) && (l) >= ((r) - (1000000/system_hz)))
 | |
| 
 | |
| #define FILLITIMER(it, vs, vu, is, iu) \
 | |
|   (it).it_value.tv_sec = (vs); \
 | |
|   (it).it_value.tv_usec = (vu); \
 | |
|   (it).it_interval.tv_sec = (is); \
 | |
|   (it).it_interval.tv_usec = (iu);
 | |
| 
 | |
| /* these two macros are not fully working for all possible values;
 | |
|  * the tests only use values that the macros can deal with, though. */
 | |
| #define EQITIMER(it, vs, vu, is, iu) \
 | |
|   ((it).it_value.tv_sec == (vs) && EQUSEC((it).it_value.tv_usec,vu) && \
 | |
|   (it).it_interval.tv_sec == (is) && (it).it_interval.tv_usec == (iu))
 | |
| 
 | |
| #define LEITIMER(it, vs, vu, is, iu) \
 | |
|   ((it).it_value.tv_sec > 0 && ((it).it_value.tv_sec < (vs) || \
 | |
|   ((it).it_value.tv_sec == (vs) && (it).it_value.tv_usec <= \
 | |
|    UPPERUSEC(vu))) && \
 | |
|   (it).it_interval.tv_sec == (is) && EQUSEC((it).it_interval.tv_usec,iu))
 | |
| 
 | |
| _PROTOTYPE(int main, (int argc, char **argv));
 | |
| _PROTOTYPE(void test, (int m, int t));
 | |
| _PROTOTYPE(void test_which, (void));
 | |
| _PROTOTYPE(void test_getset, (void));
 | |
| _PROTOTYPE(void test_neglarge, (void));
 | |
| _PROTOTYPE(void test_zero, (void));
 | |
| _PROTOTYPE(void test_timer, (void));
 | |
| _PROTOTYPE(void test_alarm, (void));
 | |
| _PROTOTYPE(void test_fork, (void));
 | |
| _PROTOTYPE(void test_exec, (void));
 | |
| _PROTOTYPE(int do_check, (void));
 | |
| _PROTOTYPE(void got_alarm, (int sig));
 | |
| _PROTOTYPE(void busy_wait, (int secs));
 | |
| _PROTOTYPE(void e, (int n));
 | |
| _PROTOTYPE(void quit, (void));
 | |
| 
 | |
| static char *executable;
 | |
| static int signals;
 | |
| static int timer;
 | |
| static int errct = 0, subtest;
 | |
| static long system_hz;
 | |
| 
 | |
| static int sigs[] = { SIGALRM, SIGVTALRM, SIGPROF };
 | |
| static const char *names[] = { "REAL", "VIRTUAL", "PROF" };
 | |
| 
 | |
| int main(argc, argv)
 | |
| int argc;
 | |
| char **argv;
 | |
| {
 | |
|   int i, m = 0xFFFF, n = 0xF;
 | |
| 
 | |
|   getsysinfo_up(PM_PROC_NR, SIU_SYSTEMHZ, sizeof(system_hz), &system_hz);
 | |
| 
 | |
|   if (strcmp(argv[0], "DO CHECK") == 0) {
 | |
|   	timer = atoi(argv[1]);
 | |
| 
 | |
| 	exit(do_check());
 | |
|   }
 | |
| 
 | |
|   printf("Test 41 ");
 | |
|   fflush(stdout);
 | |
| 
 | |
|   executable = argv[0];
 | |
| 
 | |
|   if (argc >= 2) m = atoi(argv[1]);
 | |
|   if (argc >= 3) n = atoi(argv[2]);
 | |
| 
 | |
|   for (i = 0; i < ITERATIONS; i++) {
 | |
|   	if (n & 1) test(m, ITIMER_REAL);
 | |
|   	if (n & 2) test(m, ITIMER_VIRTUAL);
 | |
|   	if (n & 4) test(m, ITIMER_PROF);
 | |
|   }
 | |
| 
 | |
|   quit();
 | |
|   return(-1);			/* impossible */
 | |
| }
 | |
| 
 | |
| void test(m, t)
 | |
| int m;
 | |
| int t;
 | |
| {
 | |
|   timer = t;
 | |
| 
 | |
|   if (m & 0001) test_which();
 | |
|   if (m & 0002) test_getset();
 | |
|   if (m & 0004) test_neglarge();
 | |
|   if (m & 0010) test_zero();
 | |
|   if (m & 0020) test_timer();
 | |
|   if (m & 0040) test_alarm();
 | |
|   if (m & 0100) test_fork();
 | |
|   if (m & 0200) test_exec();
 | |
| }
 | |
| 
 | |
| /* test invalid and unsupported 'which' values */
 | |
| void test_which()
 | |
| {
 | |
|   struct itimerval it;
 | |
| 
 | |
|   subtest = 0;
 | |
| 
 | |
|   errno = 0; if (!getitimer(-1, &it) || errno != EINVAL) e(1);
 | |
|   errno = 0; if ( getitimer(timer, &it)                ) e(2);
 | |
|   errno = 0; if (!getitimer( 3, &it) || errno != EINVAL) e(3);
 | |
| }
 | |
| 
 | |
| /* test if we get back what we set */
 | |
| void test_getset()
 | |
| {
 | |
|   struct itimerval it, oit;
 | |
| 
 | |
|   subtest = 1;
 | |
| 
 | |
|   /* no alarm should be set initially */
 | |
|   if (getitimer(timer, &it)) e(1);
 | |
|   if (!EQITIMER(it, 0, 0, 0, 0)) e(2);
 | |
| 
 | |
|   if (setitimer(timer, &it, &oit)) e(3);
 | |
|   if (setitimer(timer, &oit, NULL)) e(4);
 | |
|   if (!EQITIMER(oit, 0, 0, 0, 0)) e(5);
 | |
| 
 | |
|   FILLITIMER(it, 123, 0, 456, 0);
 | |
|   if (setitimer(timer, &it, NULL)) e(6);
 | |
| 
 | |
|   FILLITIMER(it, 987, 0, 654, 0);
 | |
|   if (setitimer(timer, &it, &oit)) e(7);
 | |
|   if (!LEITIMER(oit, 123, 0, 456, 0)) e(8);
 | |
| 
 | |
|   if (getitimer(timer, &oit)) e(9);
 | |
|   if (!LEITIMER(oit, 987, 0, 654, 0)) e(10);
 | |
| 
 | |
|   FILLITIMER(it, 0, 0, 0, 0);
 | |
|   if (setitimer(timer, &it, &oit)) e(11);
 | |
|   if (!LEITIMER(oit, 987, 0, 654, 0)) e(12);
 | |
| 
 | |
|   if (getitimer(timer, &oit)) e(13);
 | |
|   if (!EQITIMER(oit, 0, 0, 0, 0)) e(14);
 | |
| }
 | |
| 
 | |
| /* test negative/large values */
 | |
| void test_neglarge()
 | |
| {
 | |
|   struct itimerval it;
 | |
| 
 | |
|   subtest = 2;
 | |
| 
 | |
|   FILLITIMER(it, 4, 0, 5, 0);
 | |
|   if (setitimer(timer, &it, NULL)) e(1);
 | |
| 
 | |
|   FILLITIMER(it, 1000000000, 0, 0, 0);
 | |
|   if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(2);
 | |
| 
 | |
|   FILLITIMER(it, 0, 1000000, 0, 0);
 | |
|   if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(3);
 | |
| 
 | |
|   FILLITIMER(it, 0, 0, 0, 1000000);
 | |
|   if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(4);
 | |
| 
 | |
|   FILLITIMER(it, -1, 0, 0, 0);
 | |
|   if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(5);
 | |
| 
 | |
|   FILLITIMER(it, 0, -1, 0, 0);
 | |
|   if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(6);
 | |
| 
 | |
|   FILLITIMER(it, 0, 0, -1, 0);
 | |
|   if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(7);
 | |
| 
 | |
|   FILLITIMER(it, 0, 0, 0, -1);
 | |
|   if (!setitimer(timer, &it, NULL) || errno != EINVAL) e(8);
 | |
| 
 | |
|   if (getitimer(timer, &it)) e(9);
 | |
|   if (!LEITIMER(it, 4, 0, 5, 0)) e(10);
 | |
| }
 | |
| 
 | |
| /* setitimer with a zero timer has to set the interval to zero as well */
 | |
| void test_zero()
 | |
| {
 | |
|   struct itimerval it;
 | |
| 
 | |
|   subtest = 3;
 | |
| 
 | |
|   it.it_value.tv_sec = 0;
 | |
|   it.it_value.tv_usec = 0;
 | |
|   it.it_interval.tv_sec = 1;
 | |
|   it.it_interval.tv_usec = 1;
 | |
| 
 | |
|   if (setitimer(timer, &it, NULL)) e(1);
 | |
|   if (getitimer(timer, &it)) e(2);
 | |
|   if (!EQITIMER(it, 0, 0, 0, 0)) e(3);
 | |
| }
 | |
| 
 | |
| /* test actual timer functioning */
 | |
| void test_timer()
 | |
| {
 | |
|   struct itimerval it;
 | |
| 
 | |
|   subtest = 4;
 | |
| 
 | |
|   if (signal(sigs[timer], got_alarm) == SIG_ERR) e(1);
 | |
| 
 | |
|   FILLITIMER(it, 0, 1, 0, 1);
 | |
| 
 | |
|   if (setitimer(timer, &it, NULL)) e(2);
 | |
| 
 | |
|   signals = 0;
 | |
|   busy_wait(1);
 | |
| 
 | |
|   FILLITIMER(it, 0, 0, 0, 0);
 | |
|   if (setitimer(timer, &it, NULL)) e(3);
 | |
| 
 | |
|   /* we don't know how many signals we'll actually get in practice,
 | |
|    * so these checks more or less cover the extremes of the acceptable */
 | |
|   if (signals < 2) e(4);
 | |
|   if (signals > system_hz * 2) e(5);
 | |
| 
 | |
|   /* only for REAL timer can we check against the clock */
 | |
|   if (timer == ITIMER_REAL) {
 | |
| 	FILLITIMER(it, 1, 0, 0, 0);
 | |
| 	if (setitimer(timer, &it, NULL)) e(6);
 | |
| 
 | |
| 	signals = 0;
 | |
| 	busy_wait(1);
 | |
| 
 | |
|   	FILLITIMER(it, 0, 0, 0, 0);
 | |
|   	if (setitimer(timer, &it, NULL)) e(7);
 | |
| 
 | |
| 	if (signals != 1) e(8);
 | |
|   }
 | |
| 
 | |
|   signals = 0;
 | |
|   busy_wait(1);
 | |
| 
 | |
|   if (signals != 0) e(9);
 | |
| }
 | |
| 
 | |
| /* test itimer/alarm interaction */
 | |
| void test_alarm(void) {
 | |
|   struct itimerval it;
 | |
| 
 | |
|   /* only applicable for ITIMER_REAL */
 | |
|   if (timer != ITIMER_REAL) return;
 | |
| 
 | |
|   subtest = 5;
 | |
| 
 | |
|   if (signal(SIGALRM, got_alarm) == SIG_ERR) e(1);
 | |
| 
 | |
|   FILLITIMER(it, 3, 0, 1, 0);
 | |
|   if (setitimer(timer, &it, NULL)) e(2);
 | |
| 
 | |
|   if (alarm(2) != 3) e(3);
 | |
| 
 | |
|   if (getitimer(timer, &it)) e(4);
 | |
|   if (!LEITIMER(it, 2, 0, 0, 0)) e(5);
 | |
| 
 | |
|   signals = 0;
 | |
|   busy_wait(5);
 | |
| 
 | |
|   if (signals != 1) e(6);
 | |
| 
 | |
|   if (getitimer(timer, &it)) e(7);
 | |
|   if (!EQITIMER(it, 0, 0, 0, 0)) e(8);
 | |
| }
 | |
| 
 | |
| /* test that the timer is reset on forking */
 | |
| void test_fork(void) {
 | |
|   struct itimerval it, oit;
 | |
|   pid_t pid;
 | |
|   int status;
 | |
| 
 | |
|   subtest = 6;
 | |
| 
 | |
|   FILLITIMER(it, 12, 34, 56, 78);
 | |
| 
 | |
|   if (setitimer(timer, &it, NULL)) e(1);
 | |
| 
 | |
|   pid = fork();
 | |
|   if (pid < 0) e(2);
 | |
| 
 | |
|   if (pid == 0) {
 | |
|     if (getitimer(timer, &it)) exit(5);
 | |
|     if (!EQITIMER(it, 0, 0, 0, 0)) exit(6);
 | |
| 
 | |
|     exit(0);
 | |
|   }
 | |
| 
 | |
|   if (wait(&status) != pid) e(3);
 | |
|   if (!WIFEXITED(status)) e(4);
 | |
|   if (WEXITSTATUS(status) != 0) e(WEXITSTATUS(status));
 | |
| 
 | |
|   FILLITIMER(it, 0, 0, 0, 0);
 | |
|   if (setitimer(timer, &it, &oit)) e(7);
 | |
|   if (!LEITIMER(oit, 12, 34, 56, 78)) e(8);
 | |
| }
 | |
| 
 | |
| /* test if timer is carried over to exec()'ed process */
 | |
| void test_exec(void) {
 | |
|   struct itimerval it;
 | |
|   pid_t pid;
 | |
|   int status;
 | |
|   char buf[2];
 | |
| 
 | |
|   subtest = 7;
 | |
| 
 | |
|   pid = fork();
 | |
|   if (pid < 0) e(1);
 | |
| 
 | |
|   if (pid == 0) {
 | |
|     FILLITIMER(it, 3, 0, 1, 0);
 | |
|     if (setitimer(timer, &it, NULL)) exit(2);
 | |
| 
 | |
|     sprintf(buf, "%d", timer);
 | |
|     execl(executable, "DO CHECK", buf, NULL);
 | |
| 
 | |
|     exit(3);
 | |
|   }
 | |
| 
 | |
|   if (wait(&status) != pid) e(4);
 | |
|   if (WIFSIGNALED(status)) {
 | |
|     /* process should have died from corresponding signal */
 | |
|     if (WTERMSIG(status) != sigs[timer]) e(5);
 | |
|   }
 | |
|   else {
 | |
|     if (WIFEXITED(status)) e(WEXITSTATUS(status));
 | |
|     else e(6);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* procedure of the exec()'ed process */
 | |
| int do_check()
 | |
| {
 | |
|   struct itimerval it;
 | |
| 
 | |
|   if (getitimer(timer, &it)) return(81);
 | |
|   if (!LEITIMER(it, 3, 0, 1, 0)) return(82);
 | |
| 
 | |
|   busy_wait(60);
 | |
| 
 | |
|   return(83);
 | |
| }
 | |
| 
 | |
| void busy_wait(secs)
 | |
| int secs;
 | |
| {
 | |
|   time_t now, exp;
 | |
|   int i;
 | |
| 
 | |
|   exp = time(&now) + secs + 1;
 | |
| 
 | |
|   while (now < exp) {
 | |
|   	for (i = 0; i < 100000; i++);
 | |
| 
 | |
| 	time(&now);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void got_alarm(sig)
 | |
| int sig;
 | |
| {
 | |
|   if (sig != sigs[timer]) e(1001);
 | |
| 
 | |
|   signals++;
 | |
| }
 | |
| 
 | |
| void e(n)
 | |
| int n;
 | |
| {
 | |
| 
 | |
|   printf("Timer %s, subtest %d, error %d, errno %d: %s\n",
 | |
| 	names[timer], subtest, n, errno, strerror(errno));
 | |
| 
 | |
|   if (errct++ > MAX_ERROR) {
 | |
| 	printf("Too many errors; test aborted\n");
 | |
| 	exit(1);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void quit()
 | |
| {
 | |
| 
 | |
|   if (errct == 0) {
 | |
| 	printf("ok\n");
 | |
| 	exit(0);
 | |
|   } else {
 | |
| 	printf("%d errors\n", errct);
 | |
| 	exit(1);
 | |
|   }
 | |
| }
 | 
