summaryrefslogtreecommitdiff
path: root/regress
diff options
context:
space:
mode:
authoranton <anton@cvs.openbsd.org>2018-08-26 08:12:10 +0000
committeranton <anton@cvs.openbsd.org>2018-08-26 08:12:10 +0000
commit66d9ab8ac3de267ab7cc99eca36e29f0713688fe (patch)
tree24a97be5dc4050145ba82e6aed17a7a3d6d4638c /regress
parent050db69a15c8a2e2d4f8593613e7e72cb955757b (diff)
Add regress for kcov. It will only run if /dev/kcov can be opened successfully.
Diffstat (limited to 'regress')
-rw-r--r--regress/sys/dev/Makefile5
-rw-r--r--regress/sys/dev/kcov/Makefile13
-rw-r--r--regress/sys/dev/kcov/kcov.c346
3 files changed, 362 insertions, 2 deletions
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 <bsd.subdir.mk>
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 <bsd.regress.mk>
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 <anton@openbsd.org>
+ *
+ * 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 <sys/ioctl.h>
+#include <sys/kcov.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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;
+}