xref: /openbsd/regress/sys/dev/kcov/kcov.c (revision 1c537871)
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