1*9257d67bSanton /* $OpenBSD: kcov.c,v 1.2 2018/12/16 15:56:03 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> 2745297697Santon #include <stdio.h> 2845297697Santon #include <stdlib.h> 29*9257d67bSanton #include <string.h> 3045297697Santon #include <unistd.h> 3145297697Santon 3245297697Santon static int test_close(int); 3345297697Santon static int test_coverage(int); 3445297697Santon static int test_exec(int); 3545297697Santon static int test_fork(int); 3645297697Santon static int test_mode(int); 3745297697Santon static int test_open(int); 3845297697Santon 3945297697Santon static void do_syscall(void); 4045297697Santon static void dump(const unsigned long *); 4145297697Santon static void kcov_disable(int); 4245297697Santon static void kcov_enable(int); 4345297697Santon static int kcov_open(void); 4445297697Santon static __dead void usage(void); 4545297697Santon 4645297697Santon static const char *self; 4745297697Santon static unsigned long bufsize = 256 << 10; 4845297697Santon 4945297697Santon int 5045297697Santon main(int argc, char *argv[]) 5145297697Santon { 5245297697Santon struct { 5345297697Santon const char *name; 5445297697Santon int (*fn)(int); 5545297697Santon int coverage; /* test must produce coverage */ 5645297697Santon } tests[] = { 5745297697Santon { "coverage", test_coverage, 1 }, 5845297697Santon { "fork", test_fork, 1 }, 5945297697Santon { "exec", test_exec, 1 }, 6045297697Santon { "mode", test_mode, 1 }, 6145297697Santon { "open", test_open, 0 }, 6245297697Santon { "close", test_close, 0 }, 6345297697Santon { NULL, NULL, 0 }, 6445297697Santon }; 6545297697Santon unsigned long *cover; 6645297697Santon int c, fd, i; 67*9257d67bSanton int error = 0; 6845297697Santon int prereq = 0; 6945297697Santon int reexec = 0; 7045297697Santon int verbose = 0; 7145297697Santon 7245297697Santon self = argv[0]; 7345297697Santon 7445297697Santon while ((c = getopt(argc, argv, "Epv")) != -1) 7545297697Santon switch (c) { 7645297697Santon case 'E': 7745297697Santon reexec = 1; 7845297697Santon break; 7945297697Santon case 'p': 8045297697Santon prereq = 1; 8145297697Santon break; 8245297697Santon case 'v': 8345297697Santon verbose = 1; 8445297697Santon break; 8545297697Santon default: 8645297697Santon usage(); 8745297697Santon } 8845297697Santon argc -= optind; 8945297697Santon argv += optind; 9045297697Santon 9145297697Santon if (prereq) { 9245297697Santon fd = kcov_open(); 9345297697Santon close(fd); 9445297697Santon return 0; 9545297697Santon } 9645297697Santon 9745297697Santon if (reexec) { 9845297697Santon do_syscall(); 9945297697Santon return 0; 10045297697Santon } 10145297697Santon 102*9257d67bSanton if (argc != 1) 103*9257d67bSanton usage(); 104*9257d67bSanton for (i = 0; tests[i].name != NULL; i++) 105*9257d67bSanton if (strcmp(argv[0], tests[i].name) == 0) 106*9257d67bSanton break; 107*9257d67bSanton if (tests[i].name == NULL) 108*9257d67bSanton errx(1, "%s: no such test", argv[0]); 109*9257d67bSanton 11045297697Santon fd = kcov_open(); 11145297697Santon if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) 11245297697Santon err(1, "ioctl: KIOSETBUFSIZE"); 11345297697Santon cover = mmap(NULL, bufsize * sizeof(unsigned long), 11445297697Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 11545297697Santon if (cover == MAP_FAILED) 11645297697Santon err(1, "mmap"); 11745297697Santon 11845297697Santon *cover = 0; 119*9257d67bSanton error = tests[i].fn(fd); 12045297697Santon if (verbose) 12145297697Santon dump(cover); 12245297697Santon if (tests[i].coverage && *cover == 0) { 123*9257d67bSanton warnx("coverage empty (count=%lu, fd=%d)\n", *cover, fd); 124*9257d67bSanton error = 1; 12545297697Santon } else if (!tests[i].coverage && *cover != 0) { 126*9257d67bSanton warnx("coverage is not empty (count=%lu, fd=%d)\n", *cover, fd); 127*9257d67bSanton error = 1; 12845297697Santon } 12945297697Santon 13045297697Santon if (munmap(cover, bufsize * sizeof(unsigned long)) == -1) 13145297697Santon err(1, "munmap"); 13245297697Santon close(fd); 13345297697Santon 134*9257d67bSanton return error; 13545297697Santon } 13645297697Santon 13745297697Santon static __dead void 13845297697Santon usage(void) 13945297697Santon { 14045297697Santon fprintf(stderr, "usage: kcov [-Epv]\n"); 14145297697Santon exit(1); 14245297697Santon } 14345297697Santon 14445297697Santon static void 14545297697Santon do_syscall(void) 14645297697Santon { 14745297697Santon getpid(); 14845297697Santon } 14945297697Santon 15045297697Santon static void 15145297697Santon dump(const unsigned long *cover) 15245297697Santon { 15345297697Santon unsigned long i; 15445297697Santon 15545297697Santon for (i = 0; i < cover[0]; i++) 15645297697Santon printf("%lu/%lu: %p\n", i + 1, cover[0], (void *)cover[i + 1]); 15745297697Santon } 15845297697Santon 15945297697Santon static int 16045297697Santon kcov_open(void) 16145297697Santon { 16245297697Santon int fd; 16345297697Santon 16445297697Santon fd = open("/dev/kcov", O_RDWR); 16545297697Santon if (fd == -1) 16645297697Santon err(1, "open: /dev/kcov"); 16745297697Santon return fd; 16845297697Santon } 16945297697Santon 17045297697Santon static void 17145297697Santon kcov_enable(int fd) 17245297697Santon { 17345297697Santon if (ioctl(fd, KIOENABLE) == -1) 17445297697Santon err(1, "ioctl: KIOENABLE"); 17545297697Santon } 17645297697Santon 17745297697Santon static void 17845297697Santon kcov_disable(int fd) 17945297697Santon { 18045297697Santon if (ioctl(fd, KIODISABLE) == -1) 18145297697Santon err(1, "ioctl: KIODISABLE"); 18245297697Santon } 18345297697Santon 18445297697Santon /* 18545297697Santon * Close before mmap. 18645297697Santon */ 18745297697Santon static int 18845297697Santon test_close(int oldfd) 18945297697Santon { 19045297697Santon int fd; 19145297697Santon 19245297697Santon fd = kcov_open(); 19345297697Santon close(fd); 19445297697Santon return 0; 19545297697Santon } 19645297697Santon 19745297697Santon /* 19845297697Santon * Coverage of current thread. 19945297697Santon */ 20045297697Santon static int 20145297697Santon test_coverage(int fd) 20245297697Santon { 20345297697Santon kcov_enable(fd); 20445297697Santon do_syscall(); 20545297697Santon kcov_disable(fd); 20645297697Santon return 0; 20745297697Santon } 20845297697Santon 20945297697Santon /* 21045297697Santon * Coverage of thread after exec. 21145297697Santon */ 21245297697Santon static int 21345297697Santon test_exec(int fd) 21445297697Santon { 21545297697Santon pid_t pid; 21645297697Santon int status; 21745297697Santon 21845297697Santon pid = fork(); 21945297697Santon if (pid == -1) 22045297697Santon err(1, "fork"); 22145297697Santon if (pid == 0) { 22245297697Santon kcov_enable(fd); 22345297697Santon execlp(self, self, "-E", NULL); 22445297697Santon _exit(1); 22545297697Santon } 22645297697Santon 22745297697Santon if (waitpid(pid, &status, 0) == -1) 22845297697Santon err(1, "waitpid"); 22945297697Santon if (WIFSIGNALED(status)) { 23045297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 23145297697Santon return 1; 23245297697Santon } else if (WEXITSTATUS(status) != 0) { 23345297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 23445297697Santon return 1; 23545297697Santon } 23645297697Santon 23745297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 23845297697Santon kcov_enable(fd); 23945297697Santon kcov_disable(fd); 24045297697Santon 24145297697Santon return 0; 24245297697Santon } 24345297697Santon 24445297697Santon /* 24545297697Santon * Coverage of thread after fork. 24645297697Santon */ 24745297697Santon static int 24845297697Santon test_fork(int fd) 24945297697Santon { 25045297697Santon pid_t pid; 25145297697Santon int status; 25245297697Santon 25345297697Santon pid = fork(); 25445297697Santon if (pid == -1) 25545297697Santon err(1, "fork"); 25645297697Santon if (pid == 0) { 25745297697Santon kcov_enable(fd); 25845297697Santon do_syscall(); 25945297697Santon _exit(0); 26045297697Santon } 26145297697Santon 26245297697Santon if (waitpid(pid, &status, 0) == -1) 26345297697Santon err(1, "waitpid"); 26445297697Santon if (WIFSIGNALED(status)) { 26545297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 26645297697Santon return 1; 26745297697Santon } else if (WEXITSTATUS(status) != 0) { 26845297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 26945297697Santon return 1; 27045297697Santon } 27145297697Santon 27245297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 27345297697Santon kcov_enable(fd); 27445297697Santon kcov_disable(fd); 27545297697Santon 27645297697Santon return 0; 27745297697Santon } 27845297697Santon 27945297697Santon /* 28045297697Santon * Mode transitions. 28145297697Santon */ 28245297697Santon static int 28345297697Santon test_mode(int fd) 28445297697Santon { 28545297697Santon if (ioctl(fd, KIOENABLE) == -1) { 28645297697Santon warnx("KIOSETBUFSIZE -> KIOENABLE"); 28745297697Santon return 1; 28845297697Santon } 28945297697Santon if (ioctl(fd, KIODISABLE) == -1) { 29045297697Santon warnx("KIOENABLE -> KIODISABLE"); 29145297697Santon return 1; 29245297697Santon } 29345297697Santon if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { 29445297697Santon warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE"); 29545297697Santon return 1; 29645297697Santon } 29745297697Santon if (ioctl(fd, KIODISABLE) != -1) { 29845297697Santon warnx("KIOSETBUFSIZE -> KIODISABLE"); 29945297697Santon return 1; 30045297697Santon } 30145297697Santon 30245297697Santon kcov_enable(fd); 30345297697Santon if (ioctl(fd, KIOENABLE) != -1) { 30445297697Santon warnx("KIOENABLE -> KIOENABLE"); 30545297697Santon return 1; 30645297697Santon } 30745297697Santon if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { 30845297697Santon warnx("KIOENABLE -> KIOSETBUFSIZE"); 30945297697Santon return 1; 31045297697Santon } 31145297697Santon kcov_disable(fd); 31245297697Santon 31345297697Santon return 0; 31445297697Santon } 31545297697Santon 31645297697Santon /* 31745297697Santon * Open /dev/kcov more than once. 31845297697Santon */ 31945297697Santon static int 32045297697Santon test_open(int oldfd) 32145297697Santon { 32245297697Santon unsigned long *cover; 32345297697Santon int fd; 32445297697Santon int ret = 0; 32545297697Santon 32645297697Santon fd = kcov_open(); 32745297697Santon if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) 32845297697Santon err(1, "ioctl: KIOSETBUFSIZE"); 32945297697Santon cover = mmap(NULL, bufsize * sizeof(unsigned long), 33045297697Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 33145297697Santon if (cover == MAP_FAILED) 33245297697Santon err(1, "mmap"); 33345297697Santon 33445297697Santon kcov_enable(fd); 33545297697Santon do_syscall(); 33645297697Santon kcov_disable(fd); 33745297697Santon 33845297697Santon if (*cover == 0) { 33945297697Santon warnx("coverage empty (count=0, fd=%d)\n", fd); 34045297697Santon ret = 1; 34145297697Santon } 34245297697Santon if (munmap(cover, bufsize * sizeof(unsigned long))) 34345297697Santon err(1, "munmap"); 34445297697Santon close(fd); 34545297697Santon return ret; 34645297697Santon } 347