1*1c537871Santon /* $OpenBSD: kcov.c,v 1.12 2019/05/19 09:34:59 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*1c537871Santon struct context { 34*1c537871Santon int c_fd; 35*1c537871Santon int c_mode; 36*1c537871Santon unsigned long c_bufsize; 37*1c537871Santon }; 38*1c537871Santon 39*1c537871Santon static int test_close(const struct context *); 40*1c537871Santon static int test_coverage(const struct context *); 41*1c537871Santon static int test_dying(const struct context *); 42*1c537871Santon static int test_exec(const struct context *); 43*1c537871Santon static int test_fork(const struct context *); 44*1c537871Santon static int test_open(const struct context *); 45*1c537871Santon static int test_state(const struct context *); 4645297697Santon 473bb8297cSanton static int check_coverage(const unsigned long *, int, unsigned long, int); 4845297697Santon static void do_syscall(void); 493bb8297cSanton static void dump(const unsigned long *, int mode); 5045297697Santon static void kcov_disable(int); 510e2965f9Santon static void kcov_enable(int, int); 5245297697Santon static int kcov_open(void); 5345297697Santon static __dead void usage(void); 5445297697Santon 5545297697Santon static const char *self; 5645297697Santon 5745297697Santon int 5845297697Santon main(int argc, char *argv[]) 5945297697Santon { 6045297697Santon struct { 6145297697Santon const char *name; 62*1c537871Santon int (*fn)(const struct context *); 6345297697Santon int coverage; /* test must produce coverage */ 6445297697Santon } tests[] = { 6545297697Santon { "close", test_close, 0 }, 66e3865369Santon { "coverage", test_coverage, 1 }, 67aad1a0fbSanton { "dying", test_dying, 1 }, 68e3865369Santon { "exec", test_exec, 1 }, 69e3865369Santon { "fork", test_fork, 1 }, 70e3865369Santon { "open", test_open, 0 }, 71e3865369Santon { "state", test_state, 1 }, 7245297697Santon { NULL, NULL, 0 }, 7345297697Santon }; 74*1c537871Santon struct context ctx; 75078ed3e5Santon const char *errstr; 76078ed3e5Santon unsigned long *cover, frac; 77*1c537871Santon int c, i; 789257d67bSanton int error = 0; 7945297697Santon int prereq = 0; 8045297697Santon int reexec = 0; 8145297697Santon int verbose = 0; 8245297697Santon 8345297697Santon self = argv[0]; 8445297697Santon 85*1c537871Santon memset(&ctx, 0, sizeof(ctx)); 86*1c537871Santon ctx.c_bufsize = 256 << 10; 87*1c537871Santon 88078ed3e5Santon while ((c = getopt(argc, argv, "b:Em:pv")) != -1) 8945297697Santon switch (c) { 90078ed3e5Santon case 'b': 91078ed3e5Santon frac = strtonum(optarg, 1, 100, &errstr); 92078ed3e5Santon if (frac == 0) 93078ed3e5Santon errx(1, "buffer size fraction %s", errstr); 94*1c537871Santon else if (frac > ctx.c_bufsize) 95078ed3e5Santon errx(1, "buffer size fraction too large"); 96*1c537871Santon ctx.c_bufsize /= frac; 97078ed3e5Santon break; 9845297697Santon case 'E': 9945297697Santon reexec = 1; 10045297697Santon break; 1010e2965f9Santon case 'm': 1020e2965f9Santon if (strcmp(optarg, "pc") == 0) 103*1c537871Santon ctx.c_mode = KCOV_MODE_TRACE_PC; 104a4101078Santon else if (strcmp(optarg, "cmp") == 0) 105*1c537871Santon ctx.c_mode = KCOV_MODE_TRACE_CMP; 1060e2965f9Santon else 1070e2965f9Santon errx(1, "unknown mode %s", optarg); 1080e2965f9Santon break; 10945297697Santon case 'p': 11045297697Santon prereq = 1; 11145297697Santon break; 11245297697Santon case 'v': 11345297697Santon verbose = 1; 11445297697Santon break; 11545297697Santon default: 11645297697Santon usage(); 11745297697Santon } 11845297697Santon argc -= optind; 11945297697Santon argv += optind; 12045297697Santon 12145297697Santon if (prereq) { 122*1c537871Santon ctx.c_fd = kcov_open(); 123*1c537871Santon close(ctx.c_fd); 12445297697Santon return 0; 12545297697Santon } 12645297697Santon 12745297697Santon if (reexec) { 12845297697Santon do_syscall(); 12945297697Santon return 0; 13045297697Santon } 13145297697Santon 132*1c537871Santon if (ctx.c_mode == 0 || argc != 1) 1339257d67bSanton usage(); 1349257d67bSanton for (i = 0; tests[i].name != NULL; i++) 1359257d67bSanton if (strcmp(argv[0], tests[i].name) == 0) 1369257d67bSanton break; 1379257d67bSanton if (tests[i].name == NULL) 1389257d67bSanton errx(1, "%s: no such test", argv[0]); 1399257d67bSanton 140*1c537871Santon ctx.c_fd = kcov_open(); 141*1c537871Santon if (ioctl(ctx.c_fd, KIOSETBUFSIZE, &ctx.c_bufsize) == -1) 14245297697Santon err(1, "ioctl: KIOSETBUFSIZE"); 143*1c537871Santon cover = mmap(NULL, ctx.c_bufsize * sizeof(unsigned long), 144*1c537871Santon PROT_READ | PROT_WRITE, MAP_SHARED, ctx.c_fd, 0); 14545297697Santon if (cover == MAP_FAILED) 14645297697Santon err(1, "mmap"); 14745297697Santon 14845297697Santon *cover = 0; 149*1c537871Santon error = tests[i].fn(&ctx); 15045297697Santon if (verbose) 151*1c537871Santon dump(cover, ctx.c_mode); 152*1c537871Santon if (check_coverage(cover, ctx.c_mode, ctx.c_bufsize, tests[i].coverage)) 1539257d67bSanton error = 1; 15445297697Santon 155*1c537871Santon if (munmap(cover, ctx.c_bufsize * sizeof(unsigned long)) == -1) 15645297697Santon err(1, "munmap"); 157*1c537871Santon close(ctx.c_fd); 15845297697Santon 1599257d67bSanton return error; 16045297697Santon } 16145297697Santon 16245297697Santon static __dead void 16345297697Santon usage(void) 16445297697Santon { 165078ed3e5Santon fprintf(stderr, "usage: kcov [-Epv] [-b fraction] -t mode test\n"); 16645297697Santon exit(1); 16745297697Santon } 16845297697Santon 16945297697Santon static void 17045297697Santon do_syscall(void) 17145297697Santon { 17245297697Santon getpid(); 17345297697Santon } 17445297697Santon 1753bb8297cSanton static int 1763bb8297cSanton check_coverage(const unsigned long *cover, int mode, unsigned long maxsize, 1773bb8297cSanton int nonzero) 1783bb8297cSanton { 179a4101078Santon unsigned long arg1, arg2, exp, i, pc, type; 1803bb8297cSanton int error = 0; 1813bb8297cSanton 1823bb8297cSanton if (nonzero && cover[0] == 0) { 1833bb8297cSanton warnx("coverage empty (count=0)\n"); 1843bb8297cSanton return 1; 1853bb8297cSanton } else if (!nonzero && cover[0] != 0) { 1863bb8297cSanton warnx("coverage not empty (count=%lu)\n", *cover); 1873bb8297cSanton return 1; 1883bb8297cSanton } else if (cover[0] >= maxsize) { 189*1c537871Santon warnx("coverage overflow (count=%lu, max=%lu)\n", 190*1c537871Santon *cover, maxsize); 1913bb8297cSanton return 1; 1923bb8297cSanton } 1933bb8297cSanton 194a4101078Santon if (mode == KCOV_MODE_TRACE_CMP) { 195a4101078Santon if (*cover * 4 >= maxsize) { 196a4101078Santon warnx("coverage cmp overflow (count=%lu, max=%lu)\n", 197a4101078Santon *cover * 4, maxsize); 198a4101078Santon return 1; 199a4101078Santon } 200a4101078Santon 201a4101078Santon for (i = 0; i < cover[0]; i++) { 202a4101078Santon type = cover[i * 4 + 1]; 203a4101078Santon arg1 = cover[i * 4 + 2]; 204a4101078Santon arg2 = cover[i * 4 + 3]; 205a4101078Santon pc = cover[i * 4 + 4]; 206a4101078Santon 207a4101078Santon exp = type >> 1; 208a4101078Santon if (exp <= 3) 209a4101078Santon continue; 210a4101078Santon 211a4101078Santon warnx("coverage cmp invalid size (i=%lu, exp=%lx, " 212a4101078Santon "const=%ld, arg1=%lu, arg2=%lu, pc=%p)\n", 213a4101078Santon i, exp, type & 0x1, arg1, arg2, (void *)pc); 214a4101078Santon error = 1; 215a4101078Santon } 216a4101078Santon } 217a4101078Santon 2183bb8297cSanton return error; 2193bb8297cSanton } 2203bb8297cSanton 22145297697Santon static void 2223bb8297cSanton dump(const unsigned long *cover, int mode) 22345297697Santon { 22445297697Santon unsigned long i; 2253bb8297cSanton int stride = 1; 22645297697Santon 227a4101078Santon if (mode == KCOV_MODE_TRACE_CMP) 228a4101078Santon stride = 4; 229a4101078Santon 23045297697Santon for (i = 0; i < cover[0]; i++) 231db9a2a8dSanton printf("%p\n", (void *)cover[i * stride + stride]); 23245297697Santon } 23345297697Santon 23445297697Santon static int 23545297697Santon kcov_open(void) 23645297697Santon { 23745297697Santon int fd; 23845297697Santon 23945297697Santon fd = open("/dev/kcov", O_RDWR); 24045297697Santon if (fd == -1) 24145297697Santon err(1, "open: /dev/kcov"); 24245297697Santon return fd; 24345297697Santon } 24445297697Santon 24545297697Santon static void 2460e2965f9Santon kcov_enable(int fd, int mode) 24745297697Santon { 2480e2965f9Santon if (ioctl(fd, KIOENABLE, &mode) == -1) 24945297697Santon err(1, "ioctl: KIOENABLE"); 25045297697Santon } 25145297697Santon 25245297697Santon static void 25345297697Santon kcov_disable(int fd) 25445297697Santon { 25545297697Santon if (ioctl(fd, KIODISABLE) == -1) 25645297697Santon err(1, "ioctl: KIODISABLE"); 25745297697Santon } 25845297697Santon 25945297697Santon /* 26045297697Santon * Close before mmap. 26145297697Santon */ 26245297697Santon static int 263*1c537871Santon test_close(const struct context *ctx) 26445297697Santon { 26545297697Santon int fd; 26645297697Santon 26745297697Santon fd = kcov_open(); 26845297697Santon close(fd); 26945297697Santon return 0; 27045297697Santon } 27145297697Santon 27245297697Santon /* 27345297697Santon * Coverage of current thread. 27445297697Santon */ 27545297697Santon static int 276*1c537871Santon test_coverage(const struct context *ctx) 27745297697Santon { 278*1c537871Santon kcov_enable(ctx->c_fd, ctx->c_mode); 27945297697Santon do_syscall(); 280*1c537871Santon kcov_disable(ctx->c_fd); 28145297697Santon return 0; 28245297697Santon } 28345297697Santon 284aad1a0fbSanton static void * 285aad1a0fbSanton closer(void *arg) 286aad1a0fbSanton { 287*1c537871Santon const struct context *ctx = arg; 288aad1a0fbSanton 289*1c537871Santon close(ctx->c_fd); 290aad1a0fbSanton return NULL; 291aad1a0fbSanton } 292aad1a0fbSanton 293aad1a0fbSanton /* 294aad1a0fbSanton * Close kcov descriptor in another thread during tracing. 295aad1a0fbSanton */ 296aad1a0fbSanton static int 297*1c537871Santon test_dying(const struct context *ctx) 298aad1a0fbSanton { 299aad1a0fbSanton pthread_t th; 300aad1a0fbSanton int error; 301aad1a0fbSanton 302*1c537871Santon kcov_enable(ctx->c_fd, ctx->c_mode); 303aad1a0fbSanton 304*1c537871Santon if ((error = pthread_create(&th, NULL, closer, (void *)ctx))) 305aad1a0fbSanton errc(1, error, "pthread_create"); 306aad1a0fbSanton if ((error = pthread_join(th, NULL))) 307aad1a0fbSanton errc(1, error, "pthread_join"); 308aad1a0fbSanton 309*1c537871Santon if (close(ctx->c_fd) == -1) { 310aad1a0fbSanton if (errno != EBADF) 311aad1a0fbSanton err(1, "close"); 312aad1a0fbSanton } else { 313aad1a0fbSanton warnx("expected kcov descriptor to be closed"); 314aad1a0fbSanton return 1; 315aad1a0fbSanton } 316aad1a0fbSanton 317aad1a0fbSanton return 0; 318aad1a0fbSanton } 319aad1a0fbSanton 32045297697Santon /* 32145297697Santon * Coverage of thread after exec. 32245297697Santon */ 32345297697Santon static int 324*1c537871Santon test_exec(const struct context *ctx) 32545297697Santon { 32645297697Santon pid_t pid; 32745297697Santon int status; 32845297697Santon 32945297697Santon pid = fork(); 33045297697Santon if (pid == -1) 33145297697Santon err(1, "fork"); 33245297697Santon if (pid == 0) { 333*1c537871Santon kcov_enable(ctx->c_fd, ctx->c_mode); 33445297697Santon execlp(self, self, "-E", NULL); 33545297697Santon _exit(1); 33645297697Santon } 33745297697Santon 33845297697Santon if (waitpid(pid, &status, 0) == -1) 33945297697Santon err(1, "waitpid"); 34045297697Santon if (WIFSIGNALED(status)) { 34145297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 34245297697Santon return 1; 34345297697Santon } else if (WEXITSTATUS(status) != 0) { 34445297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 34545297697Santon return 1; 34645297697Santon } 34745297697Santon 34845297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 349*1c537871Santon kcov_enable(ctx->c_fd, ctx->c_mode); 350*1c537871Santon kcov_disable(ctx->c_fd); 35145297697Santon 35245297697Santon return 0; 35345297697Santon } 35445297697Santon 35545297697Santon /* 35645297697Santon * Coverage of thread after fork. 35745297697Santon */ 35845297697Santon static int 359*1c537871Santon test_fork(const struct context *ctx) 36045297697Santon { 36145297697Santon pid_t pid; 36245297697Santon int status; 36345297697Santon 36445297697Santon pid = fork(); 36545297697Santon if (pid == -1) 36645297697Santon err(1, "fork"); 36745297697Santon if (pid == 0) { 368*1c537871Santon kcov_enable(ctx->c_fd, ctx->c_mode); 36945297697Santon do_syscall(); 37045297697Santon _exit(0); 37145297697Santon } 37245297697Santon 37345297697Santon if (waitpid(pid, &status, 0) == -1) 37445297697Santon err(1, "waitpid"); 37545297697Santon if (WIFSIGNALED(status)) { 37645297697Santon warnx("terminated by signal (%d)", WTERMSIG(status)); 37745297697Santon return 1; 37845297697Santon } else if (WEXITSTATUS(status) != 0) { 37945297697Santon warnx("non-zero exit (%d)", WEXITSTATUS(status)); 38045297697Santon return 1; 38145297697Santon } 38245297697Santon 38345297697Santon /* Upon exit, the kcov descriptor must be reusable again. */ 384*1c537871Santon kcov_enable(ctx->c_fd, ctx->c_mode); 385*1c537871Santon kcov_disable(ctx->c_fd); 38645297697Santon 38745297697Santon return 0; 38845297697Santon } 38945297697Santon 39045297697Santon /* 391e3865369Santon * Open /dev/kcov more than once. 39245297697Santon */ 39345297697Santon static int 394*1c537871Santon test_open(const struct context *ctx) 395e3865369Santon { 396e3865369Santon unsigned long *cover; 397e3865369Santon int fd; 3983bb8297cSanton int error = 0; 399e3865369Santon 400e3865369Santon fd = kcov_open(); 401*1c537871Santon if (ioctl(fd, KIOSETBUFSIZE, &ctx->c_bufsize) == -1) 402e3865369Santon err(1, "ioctl: KIOSETBUFSIZE"); 403*1c537871Santon cover = mmap(NULL, ctx->c_bufsize * sizeof(unsigned long), 404e3865369Santon PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 405e3865369Santon if (cover == MAP_FAILED) 406e3865369Santon err(1, "mmap"); 407e3865369Santon 408*1c537871Santon kcov_enable(fd, ctx->c_mode); 409e3865369Santon do_syscall(); 410e3865369Santon kcov_disable(fd); 411e3865369Santon 412*1c537871Santon error = check_coverage(cover, ctx->c_mode, ctx->c_bufsize, 1); 4133bb8297cSanton 414*1c537871Santon if (munmap(cover, ctx->c_bufsize * sizeof(unsigned long))) 415e3865369Santon err(1, "munmap"); 416e3865369Santon close(fd); 4173bb8297cSanton 4183bb8297cSanton return error; 419e3865369Santon } 420e3865369Santon 421e3865369Santon /* 422e3865369Santon * State transitions. 423e3865369Santon */ 424e3865369Santon static int 425*1c537871Santon test_state(const struct context *ctx) 42645297697Santon { 427*1c537871Santon if (ioctl(ctx->c_fd, KIOENABLE, &ctx->c_mode) == -1) { 4280e2965f9Santon warn("KIOSETBUFSIZE -> KIOENABLE"); 42945297697Santon return 1; 43045297697Santon } 431*1c537871Santon if (ioctl(ctx->c_fd, KIODISABLE) == -1) { 4320e2965f9Santon warn("KIOENABLE -> KIODISABLE"); 43345297697Santon return 1; 43445297697Santon } 435*1c537871Santon if (ioctl(ctx->c_fd, KIOSETBUFSIZE, 0) != -1) { 43645297697Santon warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE"); 43745297697Santon return 1; 43845297697Santon } 439*1c537871Santon if (ioctl(ctx->c_fd, KIODISABLE) != -1) { 44045297697Santon warnx("KIOSETBUFSIZE -> KIODISABLE"); 44145297697Santon return 1; 44245297697Santon } 44345297697Santon 444*1c537871Santon kcov_enable(ctx->c_fd, ctx->c_mode); 445*1c537871Santon if (ioctl(ctx->c_fd, KIOENABLE, &ctx->c_mode) != -1) { 44645297697Santon warnx("KIOENABLE -> KIOENABLE"); 44745297697Santon return 1; 44845297697Santon } 449*1c537871Santon if (ioctl(ctx->c_fd, KIOSETBUFSIZE, 0) != -1) { 45045297697Santon warnx("KIOENABLE -> KIOSETBUFSIZE"); 45145297697Santon return 1; 45245297697Santon } 453*1c537871Santon kcov_disable(ctx->c_fd); 45445297697Santon 45545297697Santon return 0; 45645297697Santon } 457