From 45297697fa64c51212ed673763cd26cbcfdc8632 Mon Sep 17 00:00:00 2001 From: anton Date: Sun, 26 Aug 2018 08:12:09 +0000 Subject: [PATCH] Add regress for kcov. It will only run if /dev/kcov can be opened successfully. --- regress/sys/dev/Makefile | 5 +- regress/sys/dev/kcov/Makefile | 13 ++ regress/sys/dev/kcov/kcov.c | 346 ++++++++++++++++++++++++++++++++++ 3 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 regress/sys/dev/kcov/Makefile create mode 100644 regress/sys/dev/kcov/kcov.c diff --git a/regress/sys/dev/Makefile b/regress/sys/dev/Makefile index 9ae0ea16109..f24f0db521d 100644 --- a/regress/sys/dev/Makefile +++ b/regress/sys/dev/Makefile @@ -1,5 +1,6 @@ -# $OpenBSD: Makefile,v 1.4 2016/09/06 04:35:03 ratchov Exp $ +# $OpenBSD: Makefile,v 1.5 2018/08/26 08:12:09 anton Exp $ -SUBDIR+= fdesc +SUBDIR+= fdesc +SUBDIR+= kcov .include diff --git a/regress/sys/dev/kcov/Makefile b/regress/sys/dev/kcov/Makefile new file mode 100644 index 00000000000..2760219143c --- /dev/null +++ b/regress/sys/dev/kcov/Makefile @@ -0,0 +1,13 @@ +# $OpenBSD: Makefile,v 1.1 2018/08/26 08:12:09 anton Exp $ + +PROG= kcov +WARNINGS= yes + +run-regress-${PROG}: ${PROG} + if ${SUDO} ./${PROG} -p; then \ + ${SUDO} ./${PROG} ${KCOVFLAGS}; \ + else \ + echo "SKIPPED"; \ + fi + +.include diff --git a/regress/sys/dev/kcov/kcov.c b/regress/sys/dev/kcov/kcov.c new file mode 100644 index 00000000000..c6b52124091 --- /dev/null +++ b/regress/sys/dev/kcov/kcov.c @@ -0,0 +1,346 @@ +/* $OpenBSD: kcov.c,v 1.1 2018/08/26 08:12:09 anton Exp $ */ + +/* + * Copyright (c) 2018 Anton Lindqvist + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int test_close(int); +static int test_coverage(int); +static int test_exec(int); +static int test_fork(int); +static int test_mode(int); +static int test_open(int); + +static void do_syscall(void); +static void dump(const unsigned long *); +static void kcov_disable(int); +static void kcov_enable(int); +static int kcov_open(void); +static __dead void usage(void); + +static const char *self; +static unsigned long bufsize = 256 << 10; + +int +main(int argc, char *argv[]) +{ + struct { + const char *name; + int (*fn)(int); + int coverage; /* test must produce coverage */ + } tests[] = { + { "coverage", test_coverage, 1 }, + { "fork", test_fork, 1 }, + { "exec", test_exec, 1 }, + { "mode", test_mode, 1 }, + { "open", test_open, 0 }, + { "close", test_close, 0 }, + { NULL, NULL, 0 }, + }; + unsigned long *cover; + int c, fd, i; + int nfail = 0; + int prereq = 0; + int reexec = 0; + int verbose = 0; + + self = argv[0]; + + while ((c = getopt(argc, argv, "Epv")) != -1) + switch (c) { + case 'E': + reexec = 1; + break; + case 'p': + prereq = 1; + break; + case 'v': + verbose = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + if (argc > 0) + usage(); + + if (prereq) { + fd = kcov_open(); + close(fd); + return 0; + } + + if (reexec) { + do_syscall(); + return 0; + } + + fd = kcov_open(); + if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) + err(1, "ioctl: KIOSETBUFSIZE"); + cover = mmap(NULL, bufsize * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (cover == MAP_FAILED) + err(1, "mmap"); + + for (i = 0; tests[i].name != NULL; i++) { + printf("===> %s\n", tests[i].name); + *cover = 0; + nfail += tests[i].fn(fd); + if (verbose) + dump(cover); + if (tests[i].coverage && *cover == 0) { + warnx("coverage empty (count=%lu, fd=%d)\n", + *cover, fd); + nfail++; + } else if (!tests[i].coverage && *cover != 0) { + warnx("coverage is not empty (count=%lu, fd=%d)\n", + *cover, fd); + nfail++; + } + } + + if (munmap(cover, bufsize * sizeof(unsigned long)) == -1) + err(1, "munmap"); + close(fd); + + if (nfail > 0) + return 1; + return 0; +} + +static __dead void +usage(void) +{ + fprintf(stderr, "usage: kcov [-Epv]\n"); + exit(1); +} + +static void +do_syscall(void) +{ + getpid(); +} + +static void +dump(const unsigned long *cover) +{ + unsigned long i; + + for (i = 0; i < cover[0]; i++) + printf("%lu/%lu: %p\n", i + 1, cover[0], (void *)cover[i + 1]); +} + +static int +kcov_open(void) +{ + int fd; + + fd = open("/dev/kcov", O_RDWR); + if (fd == -1) + err(1, "open: /dev/kcov"); + return fd; +} + +static void +kcov_enable(int fd) +{ + if (ioctl(fd, KIOENABLE) == -1) + err(1, "ioctl: KIOENABLE"); +} + +static void +kcov_disable(int fd) +{ + if (ioctl(fd, KIODISABLE) == -1) + err(1, "ioctl: KIODISABLE"); +} + +/* + * Close before mmap. + */ +static int +test_close(int oldfd) +{ + int fd; + + fd = kcov_open(); + close(fd); + return 0; +} + +/* + * Coverage of current thread. + */ +static int +test_coverage(int fd) +{ + kcov_enable(fd); + do_syscall(); + kcov_disable(fd); + return 0; +} + +/* + * Coverage of thread after exec. + */ +static int +test_exec(int fd) +{ + pid_t pid; + int status; + + pid = fork(); + if (pid == -1) + err(1, "fork"); + if (pid == 0) { + kcov_enable(fd); + execlp(self, self, "-E", NULL); + _exit(1); + } + + if (waitpid(pid, &status, 0) == -1) + err(1, "waitpid"); + if (WIFSIGNALED(status)) { + warnx("terminated by signal (%d)", WTERMSIG(status)); + return 1; + } else if (WEXITSTATUS(status) != 0) { + warnx("non-zero exit (%d)", WEXITSTATUS(status)); + return 1; + } + + /* Upon exit, the kcov descriptor must be reusable again. */ + kcov_enable(fd); + kcov_disable(fd); + + return 0; +} + +/* + * Coverage of thread after fork. + */ +static int +test_fork(int fd) +{ + pid_t pid; + int status; + + pid = fork(); + if (pid == -1) + err(1, "fork"); + if (pid == 0) { + kcov_enable(fd); + do_syscall(); + _exit(0); + } + + if (waitpid(pid, &status, 0) == -1) + err(1, "waitpid"); + if (WIFSIGNALED(status)) { + warnx("terminated by signal (%d)", WTERMSIG(status)); + return 1; + } else if (WEXITSTATUS(status) != 0) { + warnx("non-zero exit (%d)", WEXITSTATUS(status)); + return 1; + } + + /* Upon exit, the kcov descriptor must be reusable again. */ + kcov_enable(fd); + kcov_disable(fd); + + return 0; +} + +/* + * Mode transitions. + */ +static int +test_mode(int fd) +{ + if (ioctl(fd, KIOENABLE) == -1) { + warnx("KIOSETBUFSIZE -> KIOENABLE"); + return 1; + } + if (ioctl(fd, KIODISABLE) == -1) { + warnx("KIOENABLE -> KIODISABLE"); + return 1; + } + if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { + warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE"); + return 1; + } + if (ioctl(fd, KIODISABLE) != -1) { + warnx("KIOSETBUFSIZE -> KIODISABLE"); + return 1; + } + + kcov_enable(fd); + if (ioctl(fd, KIOENABLE) != -1) { + warnx("KIOENABLE -> KIOENABLE"); + return 1; + } + if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) { + warnx("KIOENABLE -> KIOSETBUFSIZE"); + return 1; + } + kcov_disable(fd); + + return 0; +} + +/* + * Open /dev/kcov more than once. + */ +static int +test_open(int oldfd) +{ + unsigned long *cover; + int fd; + int ret = 0; + + fd = kcov_open(); + if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1) + err(1, "ioctl: KIOSETBUFSIZE"); + cover = mmap(NULL, bufsize * sizeof(unsigned long), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (cover == MAP_FAILED) + err(1, "mmap"); + + kcov_enable(fd); + do_syscall(); + kcov_disable(fd); + + if (*cover == 0) { + warnx("coverage empty (count=0, fd=%d)\n", fd); + ret = 1; + } + if (munmap(cover, bufsize * sizeof(unsigned long))) + err(1, "munmap"); + close(fd); + return ret; +} -- 2.20.1