// // test program for the alarm lab. // you can modify this file for testing, // but please make sure your kernel // modifications pass the original // versions of these tests. // #include "kernel/param.h" #include "kernel/types.h" #include "kernel/stat.h" #include "kernel/riscv.h" #include "user/user.h" void test0(); void test1(); void test2(); void test3(); void periodic(); void slow_handler(); void dummy_handler(); int main(int argc, char *argv[]) { test0(); test1(); test2(); test3(); exit(0); } volatile static int count; void periodic() { count = count + 1; printf("alarm!\n"); sigreturn(); } // tests whether the kernel calls // the alarm handler even a single time. void test0() { int i; printf("test0 start\n"); count = 0; sigalarm(2, periodic); for(i = 0; i < 1000*500000; i++){ if((i % 1000000) == 0) write(2, ".", 1); if(count > 0) break; } sigalarm(0, 0); if(count > 0){ printf("test0 passed\n"); } else { printf("\ntest0 failed: the kernel never called the alarm handler\n"); } } void __attribute__ ((noinline)) foo(int i, int *j) { if((i % 2500000) == 0) { write(2, ".", 1); } *j += 1; } // // tests that the kernel calls the handler multiple times. // // tests that, when the handler returns, it returns to // the point in the program where the timer interrupt // occurred, with all registers holding the same values they // held when the interrupt occurred. // void test1() { int i; int j; printf("test1 start\n"); count = 0; j = 0; sigalarm(2, periodic); for(i = 0; i < 500000000; i++){ if(count >= 10) break; foo(i, &j); } if(count < 10){ printf("\ntest1 failed: too few calls to the handler\n"); } else if(i != j){ // the loop should have called foo() i times, and foo() should // have incremented j once per call, so j should equal i. // once possible source of errors is that the handler may // return somewhere other than where the timer interrupt // occurred; another is that that registers may not be // restored correctly, causing i or j or the address ofj // to get an incorrect value. printf("\ntest1 failed: foo() executed fewer times than it was called\n"); } else { printf("test1 passed\n"); } } // // tests that kernel does not allow reentrant alarm calls. void test2() { int i; int pid; int status; printf("test2 start\n"); if ((pid = fork()) < 0) { printf("test2: fork failed\n"); } if (pid == 0) { count = 0; sigalarm(2, slow_handler); for(i = 0; i < 1000*500000; i++){ if((i % 1000000) == 0) write(2, ".", 1); if(count > 0) break; } if (count == 0) { printf("\ntest2 failed: alarm not called\n"); exit(1); } exit(0); } wait(&status); if (status == 0) { printf("test2 passed\n"); } } void slow_handler() { count++; printf("alarm!\n"); if (count > 1) { printf("test2 failed: alarm handler called more than once\n"); exit(1); } for (int i = 0; i < 1000*500000; i++) { asm volatile("nop"); // avoid compiler optimizing away loop } sigalarm(0, 0); sigreturn(); } // // dummy alarm handler; after running immediately uninstall // itself and finish signal handling void dummy_handler() { sigalarm(0, 0); sigreturn(); } // // tests that the return from sys_sigreturn() does not // modify the a0 register void test3() { uint64 a0; sigalarm(1, dummy_handler); printf("test3 start\n"); asm volatile("lui a5, 0"); asm volatile("addi a0, a5, 0xac" : : : "a0"); for(int i = 0; i < 500000000; i++) ; asm volatile("mv %0, a0" : "=r" (a0) ); if(a0 != 0xac) printf("test3 failed: register a0 changed\n"); else printf("test3 passed\n"); }