xref: /openbsd/regress/sys/dev/kcov/kcov.c (revision 9257d67b)
1*9257d67bSanton /*	$OpenBSD: kcov.c,v 1.2 2018/12/16 15:56:03 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>
2745297697Santon #include <stdio.h>
2845297697Santon #include <stdlib.h>
29*9257d67bSanton #include <string.h>
3045297697Santon #include <unistd.h>
3145297697Santon 
3245297697Santon static int test_close(int);
3345297697Santon static int test_coverage(int);
3445297697Santon static int test_exec(int);
3545297697Santon static int test_fork(int);
3645297697Santon static int test_mode(int);
3745297697Santon static int test_open(int);
3845297697Santon 
3945297697Santon static void do_syscall(void);
4045297697Santon static void dump(const unsigned long *);
4145297697Santon static void kcov_disable(int);
4245297697Santon static void kcov_enable(int);
4345297697Santon static int kcov_open(void);
4445297697Santon static __dead void usage(void);
4545297697Santon 
4645297697Santon static const char *self;
4745297697Santon static unsigned long bufsize = 256 << 10;
4845297697Santon 
4945297697Santon int
5045297697Santon main(int argc, char *argv[])
5145297697Santon {
5245297697Santon 	struct {
5345297697Santon 		const char *name;
5445297697Santon 		int (*fn)(int);
5545297697Santon 		int coverage;		/* test must produce coverage */
5645297697Santon 	} tests[] = {
5745297697Santon 		{ "coverage",	test_coverage,	1 },
5845297697Santon 		{ "fork",	test_fork,	1 },
5945297697Santon 		{ "exec",	test_exec,	1 },
6045297697Santon 		{ "mode",	test_mode,	1 },
6145297697Santon 		{ "open",	test_open,	0 },
6245297697Santon 		{ "close",	test_close,	0 },
6345297697Santon 		{ NULL,		NULL,		0 },
6445297697Santon 	};
6545297697Santon 	unsigned long *cover;
6645297697Santon 	int c, fd, i;
67*9257d67bSanton 	int error = 0;
6845297697Santon 	int prereq = 0;
6945297697Santon 	int reexec = 0;
7045297697Santon 	int verbose = 0;
7145297697Santon 
7245297697Santon 	self = argv[0];
7345297697Santon 
7445297697Santon 	while ((c = getopt(argc, argv, "Epv")) != -1)
7545297697Santon 		switch (c) {
7645297697Santon 		case 'E':
7745297697Santon 			reexec = 1;
7845297697Santon 			break;
7945297697Santon 		case 'p':
8045297697Santon 			prereq = 1;
8145297697Santon 			break;
8245297697Santon 		case 'v':
8345297697Santon 			verbose = 1;
8445297697Santon 			break;
8545297697Santon 		default:
8645297697Santon 			usage();
8745297697Santon 		}
8845297697Santon 	argc -= optind;
8945297697Santon 	argv += optind;
9045297697Santon 
9145297697Santon 	if (prereq) {
9245297697Santon 		fd = kcov_open();
9345297697Santon 		close(fd);
9445297697Santon 		return 0;
9545297697Santon 	}
9645297697Santon 
9745297697Santon 	if (reexec) {
9845297697Santon 		do_syscall();
9945297697Santon 		return 0;
10045297697Santon 	}
10145297697Santon 
102*9257d67bSanton 	if (argc != 1)
103*9257d67bSanton 		usage();
104*9257d67bSanton 	for (i = 0; tests[i].name != NULL; i++)
105*9257d67bSanton 		if (strcmp(argv[0], tests[i].name) == 0)
106*9257d67bSanton 			break;
107*9257d67bSanton 	if (tests[i].name == NULL)
108*9257d67bSanton 		errx(1, "%s: no such test", argv[0]);
109*9257d67bSanton 
11045297697Santon 	fd = kcov_open();
11145297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1)
11245297697Santon 		err(1, "ioctl: KIOSETBUFSIZE");
11345297697Santon 	cover = mmap(NULL, bufsize * sizeof(unsigned long),
11445297697Santon 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
11545297697Santon 	if (cover == MAP_FAILED)
11645297697Santon 		err(1, "mmap");
11745297697Santon 
11845297697Santon 	*cover = 0;
119*9257d67bSanton 	error = tests[i].fn(fd);
12045297697Santon 	if (verbose)
12145297697Santon 		dump(cover);
12245297697Santon 	if (tests[i].coverage && *cover == 0) {
123*9257d67bSanton                 warnx("coverage empty (count=%lu, fd=%d)\n", *cover, fd);
124*9257d67bSanton 		error = 1;
12545297697Santon 	} else if (!tests[i].coverage && *cover != 0) {
126*9257d67bSanton                 warnx("coverage is not empty (count=%lu, fd=%d)\n", *cover, fd);
127*9257d67bSanton 		error = 1;
12845297697Santon 	}
12945297697Santon 
13045297697Santon 	if (munmap(cover, bufsize * sizeof(unsigned long)) == -1)
13145297697Santon 		err(1, "munmap");
13245297697Santon 	close(fd);
13345297697Santon 
134*9257d67bSanton 	return error;
13545297697Santon }
13645297697Santon 
13745297697Santon static __dead void
13845297697Santon usage(void)
13945297697Santon {
14045297697Santon 	fprintf(stderr, "usage: kcov [-Epv]\n");
14145297697Santon 	exit(1);
14245297697Santon }
14345297697Santon 
14445297697Santon static void
14545297697Santon do_syscall(void)
14645297697Santon {
14745297697Santon 	getpid();
14845297697Santon }
14945297697Santon 
15045297697Santon static void
15145297697Santon dump(const unsigned long *cover)
15245297697Santon {
15345297697Santon 	unsigned long i;
15445297697Santon 
15545297697Santon 	for (i = 0; i < cover[0]; i++)
15645297697Santon 		printf("%lu/%lu: %p\n", i + 1, cover[0], (void *)cover[i + 1]);
15745297697Santon }
15845297697Santon 
15945297697Santon static int
16045297697Santon kcov_open(void)
16145297697Santon {
16245297697Santon 	int fd;
16345297697Santon 
16445297697Santon 	fd = open("/dev/kcov", O_RDWR);
16545297697Santon 	if (fd == -1)
16645297697Santon 		err(1, "open: /dev/kcov");
16745297697Santon 	return fd;
16845297697Santon }
16945297697Santon 
17045297697Santon static void
17145297697Santon kcov_enable(int fd)
17245297697Santon {
17345297697Santon 	if (ioctl(fd, KIOENABLE) == -1)
17445297697Santon 		err(1, "ioctl: KIOENABLE");
17545297697Santon }
17645297697Santon 
17745297697Santon static void
17845297697Santon kcov_disable(int fd)
17945297697Santon {
18045297697Santon 	if (ioctl(fd, KIODISABLE) == -1)
18145297697Santon 		err(1, "ioctl: KIODISABLE");
18245297697Santon }
18345297697Santon 
18445297697Santon /*
18545297697Santon  * Close before mmap.
18645297697Santon  */
18745297697Santon static int
18845297697Santon test_close(int oldfd)
18945297697Santon {
19045297697Santon 	int fd;
19145297697Santon 
19245297697Santon 	fd = kcov_open();
19345297697Santon 	close(fd);
19445297697Santon 	return 0;
19545297697Santon }
19645297697Santon 
19745297697Santon /*
19845297697Santon  * Coverage of current thread.
19945297697Santon  */
20045297697Santon static int
20145297697Santon test_coverage(int fd)
20245297697Santon {
20345297697Santon 	kcov_enable(fd);
20445297697Santon 	do_syscall();
20545297697Santon 	kcov_disable(fd);
20645297697Santon 	return 0;
20745297697Santon }
20845297697Santon 
20945297697Santon /*
21045297697Santon  * Coverage of thread after exec.
21145297697Santon  */
21245297697Santon static int
21345297697Santon test_exec(int fd)
21445297697Santon {
21545297697Santon 	pid_t pid;
21645297697Santon 	int status;
21745297697Santon 
21845297697Santon 	pid = fork();
21945297697Santon 	if (pid == -1)
22045297697Santon 		err(1, "fork");
22145297697Santon 	if (pid == 0) {
22245297697Santon 		kcov_enable(fd);
22345297697Santon 		execlp(self, self, "-E", NULL);
22445297697Santon 		_exit(1);
22545297697Santon 	}
22645297697Santon 
22745297697Santon 	if (waitpid(pid, &status, 0) == -1)
22845297697Santon 		err(1, "waitpid");
22945297697Santon 	if (WIFSIGNALED(status)) {
23045297697Santon 		warnx("terminated by signal (%d)", WTERMSIG(status));
23145297697Santon 		return 1;
23245297697Santon 	} else if (WEXITSTATUS(status) != 0) {
23345297697Santon 		warnx("non-zero exit (%d)", WEXITSTATUS(status));
23445297697Santon 		return 1;
23545297697Santon 	}
23645297697Santon 
23745297697Santon 	/* Upon exit, the kcov descriptor must be reusable again. */
23845297697Santon 	kcov_enable(fd);
23945297697Santon 	kcov_disable(fd);
24045297697Santon 
24145297697Santon 	return 0;
24245297697Santon }
24345297697Santon 
24445297697Santon /*
24545297697Santon  * Coverage of thread after fork.
24645297697Santon  */
24745297697Santon static int
24845297697Santon test_fork(int fd)
24945297697Santon {
25045297697Santon 	pid_t pid;
25145297697Santon 	int status;
25245297697Santon 
25345297697Santon 	pid = fork();
25445297697Santon 	if (pid == -1)
25545297697Santon 		err(1, "fork");
25645297697Santon 	if (pid == 0) {
25745297697Santon 		kcov_enable(fd);
25845297697Santon 		do_syscall();
25945297697Santon 		_exit(0);
26045297697Santon 	}
26145297697Santon 
26245297697Santon 	if (waitpid(pid, &status, 0) == -1)
26345297697Santon 		err(1, "waitpid");
26445297697Santon 	if (WIFSIGNALED(status)) {
26545297697Santon 		warnx("terminated by signal (%d)", WTERMSIG(status));
26645297697Santon 		return 1;
26745297697Santon 	} else if (WEXITSTATUS(status) != 0) {
26845297697Santon 		warnx("non-zero exit (%d)", WEXITSTATUS(status));
26945297697Santon 		return 1;
27045297697Santon 	}
27145297697Santon 
27245297697Santon 	/* Upon exit, the kcov descriptor must be reusable again. */
27345297697Santon 	kcov_enable(fd);
27445297697Santon 	kcov_disable(fd);
27545297697Santon 
27645297697Santon 	return 0;
27745297697Santon }
27845297697Santon 
27945297697Santon /*
28045297697Santon  * Mode transitions.
28145297697Santon  */
28245297697Santon static int
28345297697Santon test_mode(int fd)
28445297697Santon {
28545297697Santon 	if (ioctl(fd, KIOENABLE) == -1) {
28645297697Santon 		warnx("KIOSETBUFSIZE -> KIOENABLE");
28745297697Santon 		return 1;
28845297697Santon 	}
28945297697Santon 	if (ioctl(fd, KIODISABLE) == -1) {
29045297697Santon 		warnx("KIOENABLE -> KIODISABLE");
29145297697Santon 		return 1;
29245297697Santon 	}
29345297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) {
29445297697Santon 		warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE");
29545297697Santon 		return 1;
29645297697Santon 	}
29745297697Santon 	if (ioctl(fd, KIODISABLE) != -1) {
29845297697Santon 		warnx("KIOSETBUFSIZE -> KIODISABLE");
29945297697Santon 		return 1;
30045297697Santon 	}
30145297697Santon 
30245297697Santon 	kcov_enable(fd);
30345297697Santon 	if (ioctl(fd, KIOENABLE) != -1) {
30445297697Santon 		warnx("KIOENABLE -> KIOENABLE");
30545297697Santon 		return 1;
30645297697Santon 	}
30745297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) {
30845297697Santon 		warnx("KIOENABLE -> KIOSETBUFSIZE");
30945297697Santon 		return 1;
31045297697Santon 	}
31145297697Santon 	kcov_disable(fd);
31245297697Santon 
31345297697Santon 	return 0;
31445297697Santon }
31545297697Santon 
31645297697Santon /*
31745297697Santon  * Open /dev/kcov more than once.
31845297697Santon  */
31945297697Santon static int
32045297697Santon test_open(int oldfd)
32145297697Santon {
32245297697Santon 	unsigned long *cover;
32345297697Santon 	int fd;
32445297697Santon 	int ret = 0;
32545297697Santon 
32645297697Santon 	fd = kcov_open();
32745297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1)
32845297697Santon 		err(1, "ioctl: KIOSETBUFSIZE");
32945297697Santon 	cover = mmap(NULL, bufsize * sizeof(unsigned long),
33045297697Santon 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
33145297697Santon 	if (cover == MAP_FAILED)
33245297697Santon 		err(1, "mmap");
33345297697Santon 
33445297697Santon 	kcov_enable(fd);
33545297697Santon 	do_syscall();
33645297697Santon 	kcov_disable(fd);
33745297697Santon 
33845297697Santon 	if (*cover == 0) {
33945297697Santon 		warnx("coverage empty (count=0, fd=%d)\n", fd);
34045297697Santon 		ret = 1;
34145297697Santon 	}
34245297697Santon 	if (munmap(cover, bufsize * sizeof(unsigned long)))
34345297697Santon 		err(1, "munmap");
34445297697Santon 	close(fd);
34545297697Santon 	return ret;
34645297697Santon }
347