1*078ed3e5Santon /* $OpenBSD: kcov.c,v 1.11 2019/05/14 20:48:45 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 33*078ed3e5Santon static int test_close(int, int, unsigned long); 34*078ed3e5Santon static int test_coverage(int, int, unsigned long); 35*078ed3e5Santon static int test_dying(int, int, unsigned long); 36*078ed3e5Santon static int test_exec(int, int, unsigned long); 37*078ed3e5Santon static int test_fork(int, int, unsigned long); 38*078ed3e5Santon static int test_open(int, int, unsigned long); 39*078ed3e5Santon static int test_state(int, int, unsigned long); 4045297697Santon 413bb8297cSanton static int check_coverage(const unsigned long *, int, unsigned long, int); 4245297697Santon static void do_syscall(void); 433bb8297cSanton 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 5145297697Santon int 5245297697Santon main(int argc, char *argv[]) 5345297697Santon { 5445297697Santon struct { 5545297697Santon const char *name; 56*078ed3e5Santon int (*fn)(int, int, unsigned long); 5745297697Santon int coverage; /* test must produce coverage */ 5845297697Santon } tests[] = { 5945297697Santon { "close", test_close, 0 }, 60e3865369Santon { "coverage", test_coverage, 1 }, 61aad1a0fbSanton { "dying", test_dying, 1 }, 62e3865369Santon { "exec", test_exec, 1 }, 63e3865369Santon { "fork", test_fork, 1 }, 64e3865369Santon { "open", test_open, 0 }, 65e3865369Santon { "state", test_state, 1 }, 6645297697Santon { NULL, NULL, 0 }, 6745297697Santon }; 68*078ed3e5Santon const char *errstr; 69*078ed3e5Santon unsigned long *cover, frac; 70*078ed3e5Santon unsigned long bufsize = 256 << 10; 7145297697Santon int c, fd, i; 729257d67bSanton int error = 0; 730e2965f9Santon int mode = 0; 7445297697Santon int prereq = 0; 7545297697Santon int reexec = 0; 7645297697Santon int verbose = 0; 7745297697Santon 7845297697Santon self = argv[0]; 7945297697Santon 80*078ed3e5Santon while ((c = getopt(argc, argv, "b:Em:pv")) != -1) 8145297697Santon switch (c) { 82*078ed3e5Santon case 'b': 83*078ed3e5Santon frac = strtonum(optarg, 1, 100, &errstr); 84*078ed3e5Santon if (frac == 0) 85*078ed3e5Santon errx(1, "buffer size fraction %s", errstr); 86*078ed3e5Santon else if (frac > bufsize) 87*078ed3e5Santon errx(1, "buffer size fraction too large"); 88*078ed3e5Santon bufsize /= frac; 89*078ed3e5Santon break; 9045297697Santon case 'E': 9145297697Santon reexec = 1; 9245297697Santon break; 930e2965f9Santon case 'm': 940e2965f9Santon if (strcmp(optarg, "pc") == 0) 950e2965f9Santon mode = KCOV_MODE_TRACE_PC; 96a4101078Santon else if (strcmp(optarg, "cmp") == 0) 97a4101078Santon mode = KCOV_MODE_TRACE_CMP; 980e2965f9Santon else 990e2965f9Santon errx(1, "unknown mode %s", optarg); 1000e2965f9Santon break; 10145297697Santon case 'p': 10245297697Santon prereq = 1; 10345297697Santon break; 10445297697Santon case 'v': 10545297697Santon verbose = 1; 10645297697Santon break; 10745297697Santon default: 10845297697Santon usage(); 10945297697Santon } 11045297697Santon argc -= optind; 11145297697Santon argv += optind; 11245297697Santon 11345297697Santon if (prereq) { 11445297697Santon fd = kcov_open(); 11545297697Santon close(fd); 11645297697Santon return 0; 11745297697Santon } 11845297697Santon 11945297697Santon if (reexec) { 12045297697Santon do_syscall(); 12145297697Santon return 0; 12245297697Santon } 12345297697Santon 1240e2965f9Santon if (mode == 0 || argc != 1) 1259257d67bSanton usage(); 1269257d67bSanton for (i = 0; tests[i].name != NULL; i++) 1279257d67bSanton if (strcmp(argv[0], tests[i].name) == 0) 1289257d67bSanton break; 1299257d67bSanton if (tests[i].name == NULL) 1309257d67bSanton errx(1, "%s: no such test", argv[0]); 1319257d67bSanton 13245297697Santon fd = kcov_open(); 13345297697Santon if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) 13445297697Santon err(1, "ioctl: KIOSETBUFSIZE"); 13545297697Santon cover = mmap(NULL, bufsize * sizeof(unsigned long), 13645297697Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 13745297697Santon if (cover == MAP_FAILED) 13845297697Santon err(1, "mmap"); 13945297697Santon 14045297697Santon *cover = 0; 141*078ed3e5Santon error = tests[i].fn(fd, mode, bufsize); 14245297697Santon if (verbose) 1433bb8297cSanton dump(cover, mode); 1443bb8297cSanton if (check_coverage(cover, mode, bufsize, tests[i].coverage)) 1459257d67bSanton error = 1; 14645297697Santon 14745297697Santon if (munmap(cover, bufsize * sizeof(unsigned long)) == -1) 14845297697Santon err(1, "munmap"); 14945297697Santon close(fd); 15045297697Santon 1519257d67bSanton return error; 15245297697Santon } 15345297697Santon 15445297697Santon static __dead void 15545297697Santon usage(void) 15645297697Santon { 157*078ed3e5Santon fprintf(stderr, "usage: kcov [-Epv] [-b fraction] -t mode test\n"); 15845297697Santon exit(1); 15945297697Santon } 16045297697Santon 16145297697Santon static void 16245297697Santon do_syscall(void) 16345297697Santon { 16445297697Santon getpid(); 16545297697Santon } 16645297697Santon 1673bb8297cSanton static int 1683bb8297cSanton check_coverage(const unsigned long *cover, int mode, unsigned long maxsize, 1693bb8297cSanton int nonzero) 1703bb8297cSanton { 171a4101078Santon unsigned long arg1, arg2, exp, i, pc, type; 1723bb8297cSanton int error = 0; 1733bb8297cSanton 1743bb8297cSanton if (nonzero && cover[0] == 0) { 1753bb8297cSanton warnx("coverage empty (count=0)\n"); 1763bb8297cSanton return 1; 1773bb8297cSanton } else if (!nonzero && cover[0] != 0) { 1783bb8297cSanton warnx("coverage not empty (count=%lu)\n", *cover); 1793bb8297cSanton return 1; 1803bb8297cSanton } else if (cover[0] >= maxsize) { 1813bb8297cSanton warnx("coverage overflow (count=%lu, max=%lu)\n", *cover, maxsize); 1823bb8297cSanton return 1; 1833bb8297cSanton } 1843bb8297cSanton 185a4101078Santon if (mode == KCOV_MODE_TRACE_CMP) { 186a4101078Santon if (*cover * 4 >= maxsize) { 187a4101078Santon warnx("coverage cmp overflow (count=%lu, max=%lu)\n", 188a4101078Santon *cover * 4, maxsize); 189a4101078Santon return 1; 190a4101078Santon } 191a4101078Santon 192a4101078Santon for (i = 0; i < cover[0]; i++) { 193a4101078Santon type = cover[i * 4 + 1]; 194a4101078Santon arg1 = cover[i * 4 + 2]; 195a4101078Santon arg2 = cover[i * 4 + 3]; 196a4101078Santon pc = cover[i * 4 + 4]; 197a4101078Santon 198a4101078Santon exp = type >> 1; 199a4101078Santon if (exp <= 3) 200a4101078Santon continue; 201a4101078Santon 202a4101078Santon warnx("coverage cmp invalid size (i=%lu, exp=%lx, " 203a4101078Santon "const=%ld, arg1=%lu, arg2=%lu, pc=%p)\n", 204a4101078Santon i, exp, type & 0x1, arg1, arg2, (void *)pc); 205a4101078Santon error = 1; 206a4101078Santon } 207a4101078Santon } 208a4101078Santon 2093bb8297cSanton return error; 2103bb8297cSanton } 2113bb8297cSanton 21245297697Santon static void 2133bb8297cSanton dump(const unsigned long *cover, int mode) 21445297697Santon { 21545297697Santon unsigned long i; 2163bb8297cSanton int stride = 1; 21745297697Santon 218a4101078Santon if (mode == KCOV_MODE_TRACE_CMP) 219a4101078Santon stride = 4; 220a4101078Santon 22145297697Santon for (i = 0; i < cover[0]; i++) 222db9a2a8dSanton printf("%p\n", (void *)cover[i * stride + stride]); 22345297697Santon } 22445297697Santon 22545297697Santon static int 22645297697Santon kcov_open(void) 22745297697Santon { 22845297697Santon int fd; 22945297697Santon 23045297697Santon fd = open("/dev/kcov", O_RDWR); 23145297697Santon if (fd == -1) 23245297697Santon err(1, "open: /dev/kcov"); 23345297697Santon return fd; 23445297697Santon } 23545297697Santon 23645297697Santon static void 2370e2965f9Santon kcov_enable(int fd, int mode) 23845297697Santon { 2390e2965f9Santon if (ioctl(fd, KIOENABLE, &mode) == -1) 24045297697Santon err(1, "ioctl: KIOENABLE"); 24145297697Santon } 24245297697Santon 24345297697Santon static void 24445297697Santon kcov_disable(int fd) 24545297697Santon { 24645297697Santon if (ioctl(fd, KIODISABLE) == -1) 24745297697Santon err(1, "ioctl: KIODISABLE"); 24845297697Santon } 24945297697Santon 25045297697Santon /* 25145297697Santon * Close before mmap. 25245297697Santon */ 25345297697Santon static int 254*078ed3e5Santon test_close(int oldfd, int mode, unsigned long bufsize) 25545297697Santon { 25645297697Santon int fd; 25745297697Santon 25845297697Santon fd = kcov_open(); 25945297697Santon close(fd); 26045297697Santon return 0; 26145297697Santon } 26245297697Santon 26345297697Santon /* 26445297697Santon * Coverage of current thread. 26545297697Santon */ 26645297697Santon static int 267*078ed3e5Santon test_coverage(int fd, int mode, unsigned long bufsize) 26845297697Santon { 2690e2965f9Santon kcov_enable(fd, mode); 27045297697Santon do_syscall(); 27145297697Santon kcov_disable(fd); 27245297697Santon return 0; 27345297697Santon } 27445297697Santon 275aad1a0fbSanton static void * 276aad1a0fbSanton closer(void *arg) 277aad1a0fbSanton { 278aad1a0fbSanton int fd = *((int *)arg); 279aad1a0fbSanton 280aad1a0fbSanton close(fd); 281aad1a0fbSanton return NULL; 282aad1a0fbSanton } 283aad1a0fbSanton 284aad1a0fbSanton /* 285aad1a0fbSanton * Close kcov descriptor in another thread during tracing. 286aad1a0fbSanton */ 287aad1a0fbSanton static int 288*078ed3e5Santon test_dying(int fd, int mode, unsigned long bufsize) 289aad1a0fbSanton { 290aad1a0fbSanton pthread_t th; 291aad1a0fbSanton int error; 292aad1a0fbSanton 2930e2965f9Santon kcov_enable(fd, mode); 294aad1a0fbSanton 295aad1a0fbSanton if ((error = pthread_create(&th, NULL, closer, &fd))) 296aad1a0fbSanton errc(1, error, "pthread_create"); 297aad1a0fbSanton if ((error = pthread_join(th, NULL))) 298aad1a0fbSanton errc(1, error, "pthread_join"); 299aad1a0fbSanton 300aad1a0fbSanton if (close(fd) == -1) { 301aad1a0fbSanton if (errno != EBADF) 302aad1a0fbSanton err(1, "close"); 303aad1a0fbSanton } else { 304aad1a0fbSanton warnx("expected kcov descriptor to be closed"); 305aad1a0fbSanton return 1; 306aad1a0fbSanton } 307aad1a0fbSanton 308aad1a0fbSanton return 0; 309aad1a0fbSanton } 310aad1a0fbSanton 31145297697Santon /* 31245297697Santon * Coverage of thread after exec. 31345297697Santon */ 31445297697Santon static int 315*078ed3e5Santon test_exec(int fd, int mode, unsigned long bufsize) 31645297697Santon { 31745297697Santon pid_t pid; 31845297697Santon int status; 31945297697Santon 32045297697Santon pid = fork(); 32145297697Santon if (pid == -1) 32245297697Santon err(1, "fork"); 32345297697Santon if (pid == 0) { 3240e2965f9Santon kcov_enable(fd, mode); 32545297697Santon execlp(self, self, "-E", NULL); 32645297697Santon _exit(1); 32745297697Santon } 32845297697Santon 32945297697Santon if (waitpid(pid, &status, 0) == -1) 33045297697Santon err(1, "waitpid"); 33145297697Santon if (WIFSIGNALED(status)) { 33245297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 33345297697Santon return 1; 33445297697Santon } else if (WEXITSTATUS(status) != 0) { 33545297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 33645297697Santon return 1; 33745297697Santon } 33845297697Santon 33945297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 3400e2965f9Santon kcov_enable(fd, mode); 34145297697Santon kcov_disable(fd); 34245297697Santon 34345297697Santon return 0; 34445297697Santon } 34545297697Santon 34645297697Santon /* 34745297697Santon * Coverage of thread after fork. 34845297697Santon */ 34945297697Santon static int 350*078ed3e5Santon test_fork(int fd, int mode, unsigned long bufsize) 35145297697Santon { 35245297697Santon pid_t pid; 35345297697Santon int status; 35445297697Santon 35545297697Santon pid = fork(); 35645297697Santon if (pid == -1) 35745297697Santon err(1, "fork"); 35845297697Santon if (pid == 0) { 3590e2965f9Santon kcov_enable(fd, mode); 36045297697Santon do_syscall(); 36145297697Santon _exit(0); 36245297697Santon } 36345297697Santon 36445297697Santon if (waitpid(pid, &status, 0) == -1) 36545297697Santon err(1, "waitpid"); 36645297697Santon if (WIFSIGNALED(status)) { 36745297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 36845297697Santon return 1; 36945297697Santon } else if (WEXITSTATUS(status) != 0) { 37045297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 37145297697Santon return 1; 37245297697Santon } 37345297697Santon 37445297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 3750e2965f9Santon kcov_enable(fd, mode); 37645297697Santon kcov_disable(fd); 37745297697Santon 37845297697Santon return 0; 37945297697Santon } 38045297697Santon 38145297697Santon /* 382e3865369Santon * Open /dev/kcov more than once. 38345297697Santon */ 38445297697Santon static int 385*078ed3e5Santon test_open(int oldfd, int mode, unsigned long bufsize) 386e3865369Santon { 387e3865369Santon unsigned long *cover; 388e3865369Santon int fd; 3893bb8297cSanton int error = 0; 390e3865369Santon 391e3865369Santon fd = kcov_open(); 392e3865369Santon if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) 393e3865369Santon err(1, "ioctl: KIOSETBUFSIZE"); 394e3865369Santon cover = mmap(NULL, bufsize * sizeof(unsigned long), 395e3865369Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 396e3865369Santon if (cover == MAP_FAILED) 397e3865369Santon err(1, "mmap"); 398e3865369Santon 3990e2965f9Santon kcov_enable(fd, mode); 400e3865369Santon do_syscall(); 401e3865369Santon kcov_disable(fd); 402e3865369Santon 4033bb8297cSanton error = check_coverage(cover, mode, bufsize, 1); 4043bb8297cSanton 405e3865369Santon if (munmap(cover, bufsize * sizeof(unsigned long))) 406e3865369Santon err(1, "munmap"); 407e3865369Santon close(fd); 4083bb8297cSanton 4093bb8297cSanton return error; 410e3865369Santon } 411e3865369Santon 412e3865369Santon /* 413e3865369Santon * State transitions. 414e3865369Santon */ 415e3865369Santon static int 416*078ed3e5Santon test_state(int fd, int mode, unsigned long bufsize) 41745297697Santon { 4180e2965f9Santon if (ioctl(fd, KIOENABLE, &mode) == -1) { 4190e2965f9Santon warn("KIOSETBUFSIZE -> KIOENABLE"); 42045297697Santon return 1; 42145297697Santon } 42245297697Santon if (ioctl(fd, KIODISABLE) == -1) { 4230e2965f9Santon warn("KIOENABLE -> KIODISABLE"); 42445297697Santon return 1; 42545297697Santon } 42645297697Santon if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { 42745297697Santon warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE"); 42845297697Santon return 1; 42945297697Santon } 43045297697Santon if (ioctl(fd, KIODISABLE) != -1) { 43145297697Santon warnx("KIOSETBUFSIZE -> KIODISABLE"); 43245297697Santon return 1; 43345297697Santon } 43445297697Santon 4350e2965f9Santon kcov_enable(fd, mode); 4360e2965f9Santon if (ioctl(fd, KIOENABLE, &mode) != -1) { 43745297697Santon warnx("KIOENABLE -> KIOENABLE"); 43845297697Santon return 1; 43945297697Santon } 44045297697Santon if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { 44145297697Santon warnx("KIOENABLE -> KIOSETBUFSIZE"); 44245297697Santon return 1; 44345297697Santon } 44445297697Santon kcov_disable(fd); 44545297697Santon 44645297697Santon return 0; 44745297697Santon } 448