1*3bb8297cSanton /* $OpenBSD: kcov.c,v 1.6 2019/01/03 08:51:31 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> 27aad1a0fbSanton #include <pthread.h> 2845297697Santon #include <stdio.h> 2945297697Santon #include <stdlib.h> 309257d67bSanton #include <string.h> 3145297697Santon #include <unistd.h> 3245297697Santon 330e2965f9Santon static int test_close(int, int); 340e2965f9Santon static int test_coverage(int, int); 350e2965f9Santon static int test_dying(int, int); 360e2965f9Santon static int test_exec(int, int); 370e2965f9Santon static int test_fork(int, int); 380e2965f9Santon static int test_open(int, int); 390e2965f9Santon static int test_state(int, int); 4045297697Santon 41*3bb8297cSanton static int check_coverage(const unsigned long *, int, unsigned long, int); 4245297697Santon static void do_syscall(void); 43*3bb8297cSanton static void dump(const unsigned long *, int mode); 4445297697Santon static void kcov_disable(int); 450e2965f9Santon static void kcov_enable(int, int); 4645297697Santon static int kcov_open(void); 4745297697Santon static __dead void usage(void); 4845297697Santon 4945297697Santon static const char *self; 5045297697Santon static unsigned long bufsize = 256 << 10; 5145297697Santon 5245297697Santon int 5345297697Santon main(int argc, char *argv[]) 5445297697Santon { 5545297697Santon struct { 5645297697Santon const char *name; 570e2965f9Santon int (*fn)(int, int); 5845297697Santon int coverage; /* test must produce coverage */ 5945297697Santon } tests[] = { 6045297697Santon { "close", test_close, 0 }, 61e3865369Santon { "coverage", test_coverage, 1 }, 62aad1a0fbSanton { "dying", test_dying, 1 }, 63e3865369Santon { "exec", test_exec, 1 }, 64e3865369Santon { "fork", test_fork, 1 }, 65e3865369Santon { "open", test_open, 0 }, 66e3865369Santon { "state", test_state, 1 }, 6745297697Santon { NULL, NULL, 0 }, 6845297697Santon }; 6945297697Santon unsigned long *cover; 7045297697Santon int c, fd, i; 719257d67bSanton int error = 0; 720e2965f9Santon int mode = 0; 7345297697Santon int prereq = 0; 7445297697Santon int reexec = 0; 7545297697Santon int verbose = 0; 7645297697Santon 7745297697Santon self = argv[0]; 7845297697Santon 790e2965f9Santon while ((c = getopt(argc, argv, "Em:pv")) != -1) 8045297697Santon switch (c) { 8145297697Santon case 'E': 8245297697Santon reexec = 1; 8345297697Santon break; 840e2965f9Santon case 'm': 850e2965f9Santon if (strcmp(optarg, "pc") == 0) 860e2965f9Santon mode = KCOV_MODE_TRACE_PC; 870e2965f9Santon else 880e2965f9Santon errx(1, "unknown mode %s", optarg); 890e2965f9Santon break; 9045297697Santon case 'p': 9145297697Santon prereq = 1; 9245297697Santon break; 9345297697Santon case 'v': 9445297697Santon verbose = 1; 9545297697Santon break; 9645297697Santon default: 9745297697Santon usage(); 9845297697Santon } 9945297697Santon argc -= optind; 10045297697Santon argv += optind; 10145297697Santon 10245297697Santon if (prereq) { 10345297697Santon fd = kcov_open(); 10445297697Santon close(fd); 10545297697Santon return 0; 10645297697Santon } 10745297697Santon 10845297697Santon if (reexec) { 10945297697Santon do_syscall(); 11045297697Santon return 0; 11145297697Santon } 11245297697Santon 1130e2965f9Santon if (mode == 0 || argc != 1) 1149257d67bSanton usage(); 1159257d67bSanton for (i = 0; tests[i].name != NULL; i++) 1169257d67bSanton if (strcmp(argv[0], tests[i].name) == 0) 1179257d67bSanton break; 1189257d67bSanton if (tests[i].name == NULL) 1199257d67bSanton errx(1, "%s: no such test", argv[0]); 1209257d67bSanton 12145297697Santon fd = kcov_open(); 12245297697Santon if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) 12345297697Santon err(1, "ioctl: KIOSETBUFSIZE"); 12445297697Santon cover = mmap(NULL, bufsize * sizeof(unsigned long), 12545297697Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 12645297697Santon if (cover == MAP_FAILED) 12745297697Santon err(1, "mmap"); 12845297697Santon 12945297697Santon *cover = 0; 1300e2965f9Santon error = tests[i].fn(fd, mode); 13145297697Santon if (verbose) 132*3bb8297cSanton dump(cover, mode); 133*3bb8297cSanton if (check_coverage(cover, mode, bufsize, tests[i].coverage)) 1349257d67bSanton error = 1; 13545297697Santon 13645297697Santon if (munmap(cover, bufsize * sizeof(unsigned long)) == -1) 13745297697Santon err(1, "munmap"); 13845297697Santon close(fd); 13945297697Santon 1409257d67bSanton return error; 14145297697Santon } 14245297697Santon 14345297697Santon static __dead void 14445297697Santon usage(void) 14545297697Santon { 1460e2965f9Santon fprintf(stderr, "usage: kcov [-Epv] -t mode test\n"); 14745297697Santon exit(1); 14845297697Santon } 14945297697Santon 15045297697Santon static void 15145297697Santon do_syscall(void) 15245297697Santon { 15345297697Santon getpid(); 15445297697Santon } 15545297697Santon 156*3bb8297cSanton static int 157*3bb8297cSanton check_coverage(const unsigned long *cover, int mode, unsigned long maxsize, 158*3bb8297cSanton int nonzero) 159*3bb8297cSanton { 160*3bb8297cSanton int error = 0; 161*3bb8297cSanton 162*3bb8297cSanton if (nonzero && cover[0] == 0) { 163*3bb8297cSanton warnx("coverage empty (count=0)\n"); 164*3bb8297cSanton return 1; 165*3bb8297cSanton } else if (!nonzero && cover[0] != 0) { 166*3bb8297cSanton warnx("coverage not empty (count=%lu)\n", *cover); 167*3bb8297cSanton return 1; 168*3bb8297cSanton } else if (cover[0] >= maxsize) { 169*3bb8297cSanton warnx("coverage overflow (count=%lu, max=%lu)\n", *cover, maxsize); 170*3bb8297cSanton return 1; 171*3bb8297cSanton } 172*3bb8297cSanton 173*3bb8297cSanton return error; 174*3bb8297cSanton } 175*3bb8297cSanton 17645297697Santon static void 177*3bb8297cSanton dump(const unsigned long *cover, int mode) 17845297697Santon { 17945297697Santon unsigned long i; 180*3bb8297cSanton int stride = 1; 18145297697Santon 18245297697Santon for (i = 0; i < cover[0]; i++) 183*3bb8297cSanton printf("%p\n", (void *)cover[i * stride + 1]); 18445297697Santon } 18545297697Santon 18645297697Santon static int 18745297697Santon kcov_open(void) 18845297697Santon { 18945297697Santon int fd; 19045297697Santon 19145297697Santon fd = open("/dev/kcov", O_RDWR); 19245297697Santon if (fd == -1) 19345297697Santon err(1, "open: /dev/kcov"); 19445297697Santon return fd; 19545297697Santon } 19645297697Santon 19745297697Santon static void 1980e2965f9Santon kcov_enable(int fd, int mode) 19945297697Santon { 2000e2965f9Santon if (ioctl(fd, KIOENABLE, &mode) == -1) 20145297697Santon err(1, "ioctl: KIOENABLE"); 20245297697Santon } 20345297697Santon 20445297697Santon static void 20545297697Santon kcov_disable(int fd) 20645297697Santon { 20745297697Santon if (ioctl(fd, KIODISABLE) == -1) 20845297697Santon err(1, "ioctl: KIODISABLE"); 20945297697Santon } 21045297697Santon 21145297697Santon /* 21245297697Santon * Close before mmap. 21345297697Santon */ 21445297697Santon static int 2150e2965f9Santon test_close(int oldfd, int mode) 21645297697Santon { 21745297697Santon int fd; 21845297697Santon 21945297697Santon fd = kcov_open(); 22045297697Santon close(fd); 22145297697Santon return 0; 22245297697Santon } 22345297697Santon 22445297697Santon /* 22545297697Santon * Coverage of current thread. 22645297697Santon */ 22745297697Santon static int 2280e2965f9Santon test_coverage(int fd, int mode) 22945297697Santon { 2300e2965f9Santon kcov_enable(fd, mode); 23145297697Santon do_syscall(); 23245297697Santon kcov_disable(fd); 23345297697Santon return 0; 23445297697Santon } 23545297697Santon 236aad1a0fbSanton static void * 237aad1a0fbSanton closer(void *arg) 238aad1a0fbSanton { 239aad1a0fbSanton int fd = *((int *)arg); 240aad1a0fbSanton 241aad1a0fbSanton close(fd); 242aad1a0fbSanton return NULL; 243aad1a0fbSanton } 244aad1a0fbSanton 245aad1a0fbSanton /* 246aad1a0fbSanton * Close kcov descriptor in another thread during tracing. 247aad1a0fbSanton */ 248aad1a0fbSanton static int 2490e2965f9Santon test_dying(int fd, int mode) 250aad1a0fbSanton { 251aad1a0fbSanton pthread_t th; 252aad1a0fbSanton int error; 253aad1a0fbSanton 2540e2965f9Santon kcov_enable(fd, mode); 255aad1a0fbSanton 256aad1a0fbSanton if ((error = pthread_create(&th, NULL, closer, &fd))) 257aad1a0fbSanton errc(1, error, "pthread_create"); 258aad1a0fbSanton if ((error = pthread_join(th, NULL))) 259aad1a0fbSanton errc(1, error, "pthread_join"); 260aad1a0fbSanton 261aad1a0fbSanton if (close(fd) == -1) { 262aad1a0fbSanton if (errno != EBADF) 263aad1a0fbSanton err(1, "close"); 264aad1a0fbSanton } else { 265aad1a0fbSanton warnx("expected kcov descriptor to be closed"); 266aad1a0fbSanton return 1; 267aad1a0fbSanton } 268aad1a0fbSanton 269aad1a0fbSanton return 0; 270aad1a0fbSanton } 271aad1a0fbSanton 27245297697Santon /* 27345297697Santon * Coverage of thread after exec. 27445297697Santon */ 27545297697Santon static int 2760e2965f9Santon test_exec(int fd, int mode) 27745297697Santon { 27845297697Santon pid_t pid; 27945297697Santon int status; 28045297697Santon 28145297697Santon pid = fork(); 28245297697Santon if (pid == -1) 28345297697Santon err(1, "fork"); 28445297697Santon if (pid == 0) { 2850e2965f9Santon kcov_enable(fd, mode); 28645297697Santon execlp(self, self, "-E", NULL); 28745297697Santon _exit(1); 28845297697Santon } 28945297697Santon 29045297697Santon if (waitpid(pid, &status, 0) == -1) 29145297697Santon err(1, "waitpid"); 29245297697Santon if (WIFSIGNALED(status)) { 29345297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 29445297697Santon return 1; 29545297697Santon } else if (WEXITSTATUS(status) != 0) { 29645297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 29745297697Santon return 1; 29845297697Santon } 29945297697Santon 30045297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 3010e2965f9Santon kcov_enable(fd, mode); 30245297697Santon kcov_disable(fd); 30345297697Santon 30445297697Santon return 0; 30545297697Santon } 30645297697Santon 30745297697Santon /* 30845297697Santon * Coverage of thread after fork. 30945297697Santon */ 31045297697Santon static int 3110e2965f9Santon test_fork(int fd, int mode) 31245297697Santon { 31345297697Santon pid_t pid; 31445297697Santon int status; 31545297697Santon 31645297697Santon pid = fork(); 31745297697Santon if (pid == -1) 31845297697Santon err(1, "fork"); 31945297697Santon if (pid == 0) { 3200e2965f9Santon kcov_enable(fd, mode); 32145297697Santon do_syscall(); 32245297697Santon _exit(0); 32345297697Santon } 32445297697Santon 32545297697Santon if (waitpid(pid, &status, 0) == -1) 32645297697Santon err(1, "waitpid"); 32745297697Santon if (WIFSIGNALED(status)) { 32845297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 32945297697Santon return 1; 33045297697Santon } else if (WEXITSTATUS(status) != 0) { 33145297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 33245297697Santon return 1; 33345297697Santon } 33445297697Santon 33545297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 3360e2965f9Santon kcov_enable(fd, mode); 33745297697Santon kcov_disable(fd); 33845297697Santon 33945297697Santon return 0; 34045297697Santon } 34145297697Santon 34245297697Santon /* 343e3865369Santon * Open /dev/kcov more than once. 34445297697Santon */ 34545297697Santon static int 3460e2965f9Santon test_open(int oldfd, int mode) 347e3865369Santon { 348e3865369Santon unsigned long *cover; 349e3865369Santon int fd; 350*3bb8297cSanton int error = 0; 351e3865369Santon 352e3865369Santon fd = kcov_open(); 353e3865369Santon if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) 354e3865369Santon err(1, "ioctl: KIOSETBUFSIZE"); 355e3865369Santon cover = mmap(NULL, bufsize * sizeof(unsigned long), 356e3865369Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 357e3865369Santon if (cover == MAP_FAILED) 358e3865369Santon err(1, "mmap"); 359e3865369Santon 3600e2965f9Santon kcov_enable(fd, mode); 361e3865369Santon do_syscall(); 362e3865369Santon kcov_disable(fd); 363e3865369Santon 364*3bb8297cSanton error = check_coverage(cover, mode, bufsize, 1); 365*3bb8297cSanton 366e3865369Santon if (munmap(cover, bufsize * sizeof(unsigned long))) 367e3865369Santon err(1, "munmap"); 368e3865369Santon close(fd); 369*3bb8297cSanton 370*3bb8297cSanton return error; 371e3865369Santon } 372e3865369Santon 373e3865369Santon /* 374e3865369Santon * State transitions. 375e3865369Santon */ 376e3865369Santon static int 3770e2965f9Santon test_state(int fd, int mode) 37845297697Santon { 3790e2965f9Santon if (ioctl(fd, KIOENABLE, &mode) == -1) { 3800e2965f9Santon warn("KIOSETBUFSIZE -> KIOENABLE"); 38145297697Santon return 1; 38245297697Santon } 38345297697Santon if (ioctl(fd, KIODISABLE) == -1) { 3840e2965f9Santon warn("KIOENABLE -> KIODISABLE"); 38545297697Santon return 1; 38645297697Santon } 38745297697Santon if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { 38845297697Santon warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE"); 38945297697Santon return 1; 39045297697Santon } 39145297697Santon if (ioctl(fd, KIODISABLE) != -1) { 39245297697Santon warnx("KIOSETBUFSIZE -> KIODISABLE"); 39345297697Santon return 1; 39445297697Santon } 39545297697Santon 3960e2965f9Santon kcov_enable(fd, mode); 3970e2965f9Santon if (ioctl(fd, KIOENABLE, &mode) != -1) { 39845297697Santon warnx("KIOENABLE -> KIOENABLE"); 39945297697Santon return 1; 40045297697Santon } 40145297697Santon if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { 40245297697Santon warnx("KIOENABLE -> KIOSETBUFSIZE"); 40345297697Santon return 1; 40445297697Santon } 40545297697Santon kcov_disable(fd); 40645297697Santon 40745297697Santon return 0; 40845297697Santon } 409