1*aad1a0fbSanton /* $OpenBSD: kcov.c,v 1.3 2018/12/25 22:57:58 anton Exp $ */ 245297697Santon 345297697Santon /* 445297697Santon * Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org> 545297697Santon * 645297697Santon * Permission to use, copy, modify, and distribute this software for any 745297697Santon * purpose with or without fee is hereby granted, provided that the above 845297697Santon * copyright notice and this permission notice appear in all copies. 945297697Santon * 1045297697Santon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1145297697Santon * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1245297697Santon * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1345297697Santon * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1445297697Santon * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1545297697Santon * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1645297697Santon * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1745297697Santon */ 1845297697Santon 1945297697Santon #include <sys/ioctl.h> 2045297697Santon #include <sys/kcov.h> 2145297697Santon #include <sys/mman.h> 2245297697Santon #include <sys/wait.h> 2345297697Santon 2445297697Santon #include <err.h> 2545297697Santon #include <errno.h> 2645297697Santon #include <fcntl.h> 27*aad1a0fbSanton #include <pthread.h> 2845297697Santon #include <stdio.h> 2945297697Santon #include <stdlib.h> 309257d67bSanton #include <string.h> 3145297697Santon #include <unistd.h> 3245297697Santon 3345297697Santon static int test_close(int); 3445297697Santon static int test_coverage(int); 35*aad1a0fbSanton static int test_dying(int); 3645297697Santon static int test_exec(int); 3745297697Santon static int test_fork(int); 3845297697Santon static int test_mode(int); 3945297697Santon static int test_open(int); 4045297697Santon 4145297697Santon static void do_syscall(void); 4245297697Santon static void dump(const unsigned long *); 4345297697Santon static void kcov_disable(int); 4445297697Santon static void kcov_enable(int); 4545297697Santon static int kcov_open(void); 4645297697Santon static __dead void usage(void); 4745297697Santon 4845297697Santon static const char *self; 4945297697Santon static unsigned long bufsize = 256 << 10; 5045297697Santon 5145297697Santon int 5245297697Santon main(int argc, char *argv[]) 5345297697Santon { 5445297697Santon struct { 5545297697Santon const char *name; 5645297697Santon int (*fn)(int); 5745297697Santon int coverage; /* test must produce coverage */ 5845297697Santon } tests[] = { 5945297697Santon { "coverage", test_coverage, 1 }, 6045297697Santon { "fork", test_fork, 1 }, 6145297697Santon { "exec", test_exec, 1 }, 6245297697Santon { "mode", test_mode, 1 }, 6345297697Santon { "open", test_open, 0 }, 6445297697Santon { "close", test_close, 0 }, 65*aad1a0fbSanton { "dying", test_dying, 1 }, 6645297697Santon { NULL, NULL, 0 }, 6745297697Santon }; 6845297697Santon unsigned long *cover; 6945297697Santon int c, fd, i; 709257d67bSanton int error = 0; 7145297697Santon int prereq = 0; 7245297697Santon int reexec = 0; 7345297697Santon int verbose = 0; 7445297697Santon 7545297697Santon self = argv[0]; 7645297697Santon 7745297697Santon while ((c = getopt(argc, argv, "Epv")) != -1) 7845297697Santon switch (c) { 7945297697Santon case 'E': 8045297697Santon reexec = 1; 8145297697Santon break; 8245297697Santon case 'p': 8345297697Santon prereq = 1; 8445297697Santon break; 8545297697Santon case 'v': 8645297697Santon verbose = 1; 8745297697Santon break; 8845297697Santon default: 8945297697Santon usage(); 9045297697Santon } 9145297697Santon argc -= optind; 9245297697Santon argv += optind; 9345297697Santon 9445297697Santon if (prereq) { 9545297697Santon fd = kcov_open(); 9645297697Santon close(fd); 9745297697Santon return 0; 9845297697Santon } 9945297697Santon 10045297697Santon if (reexec) { 10145297697Santon do_syscall(); 10245297697Santon return 0; 10345297697Santon } 10445297697Santon 1059257d67bSanton if (argc != 1) 1069257d67bSanton usage(); 1079257d67bSanton for (i = 0; tests[i].name != NULL; i++) 1089257d67bSanton if (strcmp(argv[0], tests[i].name) == 0) 1099257d67bSanton break; 1109257d67bSanton if (tests[i].name == NULL) 1119257d67bSanton errx(1, "%s: no such test", argv[0]); 1129257d67bSanton 11345297697Santon fd = kcov_open(); 11445297697Santon if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) 11545297697Santon err(1, "ioctl: KIOSETBUFSIZE"); 11645297697Santon cover = mmap(NULL, bufsize * sizeof(unsigned long), 11745297697Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 11845297697Santon if (cover == MAP_FAILED) 11945297697Santon err(1, "mmap"); 12045297697Santon 12145297697Santon *cover = 0; 1229257d67bSanton error = tests[i].fn(fd); 12345297697Santon if (verbose) 12445297697Santon dump(cover); 12545297697Santon if (tests[i].coverage && *cover == 0) { 1269257d67bSanton warnx("coverage empty (count=%lu, fd=%d)\n", *cover, fd); 1279257d67bSanton error = 1; 12845297697Santon } else if (!tests[i].coverage && *cover != 0) { 1299257d67bSanton warnx("coverage is not empty (count=%lu, fd=%d)\n", *cover, fd); 1309257d67bSanton error = 1; 13145297697Santon } 13245297697Santon 13345297697Santon if (munmap(cover, bufsize * sizeof(unsigned long)) == -1) 13445297697Santon err(1, "munmap"); 13545297697Santon close(fd); 13645297697Santon 1379257d67bSanton return error; 13845297697Santon } 13945297697Santon 14045297697Santon static __dead void 14145297697Santon usage(void) 14245297697Santon { 14345297697Santon fprintf(stderr, "usage: kcov [-Epv]\n"); 14445297697Santon exit(1); 14545297697Santon } 14645297697Santon 14745297697Santon static void 14845297697Santon do_syscall(void) 14945297697Santon { 15045297697Santon getpid(); 15145297697Santon } 15245297697Santon 15345297697Santon static void 15445297697Santon dump(const unsigned long *cover) 15545297697Santon { 15645297697Santon unsigned long i; 15745297697Santon 15845297697Santon for (i = 0; i < cover[0]; i++) 15945297697Santon printf("%lu/%lu: %p\n", i + 1, cover[0], (void *)cover[i + 1]); 16045297697Santon } 16145297697Santon 16245297697Santon static int 16345297697Santon kcov_open(void) 16445297697Santon { 16545297697Santon int fd; 16645297697Santon 16745297697Santon fd = open("/dev/kcov", O_RDWR); 16845297697Santon if (fd == -1) 16945297697Santon err(1, "open: /dev/kcov"); 17045297697Santon return fd; 17145297697Santon } 17245297697Santon 17345297697Santon static void 17445297697Santon kcov_enable(int fd) 17545297697Santon { 17645297697Santon if (ioctl(fd, KIOENABLE) == -1) 17745297697Santon err(1, "ioctl: KIOENABLE"); 17845297697Santon } 17945297697Santon 18045297697Santon static void 18145297697Santon kcov_disable(int fd) 18245297697Santon { 18345297697Santon if (ioctl(fd, KIODISABLE) == -1) 18445297697Santon err(1, "ioctl: KIODISABLE"); 18545297697Santon } 18645297697Santon 18745297697Santon /* 18845297697Santon * Close before mmap. 18945297697Santon */ 19045297697Santon static int 19145297697Santon test_close(int oldfd) 19245297697Santon { 19345297697Santon int fd; 19445297697Santon 19545297697Santon fd = kcov_open(); 19645297697Santon close(fd); 19745297697Santon return 0; 19845297697Santon } 19945297697Santon 20045297697Santon /* 20145297697Santon * Coverage of current thread. 20245297697Santon */ 20345297697Santon static int 20445297697Santon test_coverage(int fd) 20545297697Santon { 20645297697Santon kcov_enable(fd); 20745297697Santon do_syscall(); 20845297697Santon kcov_disable(fd); 20945297697Santon return 0; 21045297697Santon } 21145297697Santon 212*aad1a0fbSanton static void * 213*aad1a0fbSanton closer(void *arg) 214*aad1a0fbSanton { 215*aad1a0fbSanton int fd = *((int *)arg); 216*aad1a0fbSanton 217*aad1a0fbSanton close(fd); 218*aad1a0fbSanton return NULL; 219*aad1a0fbSanton } 220*aad1a0fbSanton 221*aad1a0fbSanton /* 222*aad1a0fbSanton * Close kcov descriptor in another thread during tracing. 223*aad1a0fbSanton */ 224*aad1a0fbSanton static int 225*aad1a0fbSanton test_dying(int fd) 226*aad1a0fbSanton { 227*aad1a0fbSanton pthread_t th; 228*aad1a0fbSanton int error; 229*aad1a0fbSanton 230*aad1a0fbSanton kcov_enable(fd); 231*aad1a0fbSanton 232*aad1a0fbSanton if ((error = pthread_create(&th, NULL, closer, &fd))) 233*aad1a0fbSanton errc(1, error, "pthread_create"); 234*aad1a0fbSanton if ((error = pthread_join(th, NULL))) 235*aad1a0fbSanton errc(1, error, "pthread_join"); 236*aad1a0fbSanton 237*aad1a0fbSanton if (close(fd) == -1) { 238*aad1a0fbSanton if (errno != EBADF) 239*aad1a0fbSanton err(1, "close"); 240*aad1a0fbSanton } else { 241*aad1a0fbSanton warnx("expected kcov descriptor to be closed"); 242*aad1a0fbSanton return 1; 243*aad1a0fbSanton } 244*aad1a0fbSanton 245*aad1a0fbSanton return 0; 246*aad1a0fbSanton } 247*aad1a0fbSanton 24845297697Santon /* 24945297697Santon * Coverage of thread after exec. 25045297697Santon */ 25145297697Santon static int 25245297697Santon test_exec(int fd) 25345297697Santon { 25445297697Santon pid_t pid; 25545297697Santon int status; 25645297697Santon 25745297697Santon pid = fork(); 25845297697Santon if (pid == -1) 25945297697Santon err(1, "fork"); 26045297697Santon if (pid == 0) { 26145297697Santon kcov_enable(fd); 26245297697Santon execlp(self, self, "-E", NULL); 26345297697Santon _exit(1); 26445297697Santon } 26545297697Santon 26645297697Santon if (waitpid(pid, &status, 0) == -1) 26745297697Santon err(1, "waitpid"); 26845297697Santon if (WIFSIGNALED(status)) { 26945297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 27045297697Santon return 1; 27145297697Santon } else if (WEXITSTATUS(status) != 0) { 27245297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 27345297697Santon return 1; 27445297697Santon } 27545297697Santon 27645297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 27745297697Santon kcov_enable(fd); 27845297697Santon kcov_disable(fd); 27945297697Santon 28045297697Santon return 0; 28145297697Santon } 28245297697Santon 28345297697Santon /* 28445297697Santon * Coverage of thread after fork. 28545297697Santon */ 28645297697Santon static int 28745297697Santon test_fork(int fd) 28845297697Santon { 28945297697Santon pid_t pid; 29045297697Santon int status; 29145297697Santon 29245297697Santon pid = fork(); 29345297697Santon if (pid == -1) 29445297697Santon err(1, "fork"); 29545297697Santon if (pid == 0) { 29645297697Santon kcov_enable(fd); 29745297697Santon do_syscall(); 29845297697Santon _exit(0); 29945297697Santon } 30045297697Santon 30145297697Santon if (waitpid(pid, &status, 0) == -1) 30245297697Santon err(1, "waitpid"); 30345297697Santon if (WIFSIGNALED(status)) { 30445297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 30545297697Santon return 1; 30645297697Santon } else if (WEXITSTATUS(status) != 0) { 30745297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 30845297697Santon return 1; 30945297697Santon } 31045297697Santon 31145297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 31245297697Santon kcov_enable(fd); 31345297697Santon kcov_disable(fd); 31445297697Santon 31545297697Santon return 0; 31645297697Santon } 31745297697Santon 31845297697Santon /* 31945297697Santon * Mode transitions. 32045297697Santon */ 32145297697Santon static int 32245297697Santon test_mode(int fd) 32345297697Santon { 32445297697Santon if (ioctl(fd, KIOENABLE) == -1) { 32545297697Santon warnx("KIOSETBUFSIZE -> KIOENABLE"); 32645297697Santon return 1; 32745297697Santon } 32845297697Santon if (ioctl(fd, KIODISABLE) == -1) { 32945297697Santon warnx("KIOENABLE -> KIODISABLE"); 33045297697Santon return 1; 33145297697Santon } 33245297697Santon if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { 33345297697Santon warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE"); 33445297697Santon return 1; 33545297697Santon } 33645297697Santon if (ioctl(fd, KIODISABLE) != -1) { 33745297697Santon warnx("KIOSETBUFSIZE -> KIODISABLE"); 33845297697Santon return 1; 33945297697Santon } 34045297697Santon 34145297697Santon kcov_enable(fd); 34245297697Santon if (ioctl(fd, KIOENABLE) != -1) { 34345297697Santon warnx("KIOENABLE -> KIOENABLE"); 34445297697Santon return 1; 34545297697Santon } 34645297697Santon if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { 34745297697Santon warnx("KIOENABLE -> KIOSETBUFSIZE"); 34845297697Santon return 1; 34945297697Santon } 35045297697Santon kcov_disable(fd); 35145297697Santon 35245297697Santon return 0; 35345297697Santon } 35445297697Santon 35545297697Santon /* 35645297697Santon * Open /dev/kcov more than once. 35745297697Santon */ 35845297697Santon static int 35945297697Santon test_open(int oldfd) 36045297697Santon { 36145297697Santon unsigned long *cover; 36245297697Santon int fd; 36345297697Santon int ret = 0; 36445297697Santon 36545297697Santon fd = kcov_open(); 36645297697Santon if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) 36745297697Santon err(1, "ioctl: KIOSETBUFSIZE"); 36845297697Santon cover = mmap(NULL, bufsize * sizeof(unsigned long), 36945297697Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 37045297697Santon if (cover == MAP_FAILED) 37145297697Santon err(1, "mmap"); 37245297697Santon 37345297697Santon kcov_enable(fd); 37445297697Santon do_syscall(); 37545297697Santon kcov_disable(fd); 37645297697Santon 37745297697Santon if (*cover == 0) { 37845297697Santon warnx("coverage empty (count=0, fd=%d)\n", fd); 37945297697Santon ret = 1; 38045297697Santon } 38145297697Santon if (munmap(cover, bufsize * sizeof(unsigned long))) 38245297697Santon err(1, "munmap"); 38345297697Santon close(fd); 38445297697Santon return ret; 38545297697Santon } 386