xref: /openbsd/regress/sys/dev/kcov/kcov.c (revision a4101078)
1*a4101078Santon /*	$OpenBSD: kcov.c,v 1.9 2019/01/20 10:02:38 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 
330e2965f9Santon static int test_close(int, int);
340e2965f9Santon static int test_coverage(int, int);
350e2965f9Santon static int test_dying(int, int);
360e2965f9Santon static int test_exec(int, int);
370e2965f9Santon static int test_fork(int, int);
380e2965f9Santon static int test_open(int, int);
390e2965f9Santon static int test_state(int, int);
4045297697Santon 
413bb8297cSanton static int check_coverage(const unsigned long *, int, unsigned long, int);
4245297697Santon static void do_syscall(void);
433bb8297cSanton static void dump(const unsigned long *, int mode);
4445297697Santon static void kcov_disable(int);
450e2965f9Santon static void kcov_enable(int, int);
4645297697Santon static int kcov_open(void);
4745297697Santon static __dead void usage(void);
4845297697Santon 
4945297697Santon static const char *self;
5045297697Santon static unsigned long bufsize = 256 << 10;
5145297697Santon 
5245297697Santon int
5345297697Santon main(int argc, char *argv[])
5445297697Santon {
5545297697Santon 	struct {
5645297697Santon 		const char *name;
570e2965f9Santon 		int (*fn)(int, int);
5845297697Santon 		int coverage;		/* test must produce coverage */
5945297697Santon 	} tests[] = {
6045297697Santon 		{ "close",	test_close,	0 },
61e3865369Santon 		{ "coverage",	test_coverage,	1 },
62aad1a0fbSanton 		{ "dying",	test_dying,	1 },
63e3865369Santon 		{ "exec",	test_exec,	1 },
64e3865369Santon 		{ "fork",	test_fork,	1 },
65e3865369Santon 		{ "open",	test_open,	0 },
66e3865369Santon 		{ "state",	test_state,	1 },
6745297697Santon 		{ NULL,		NULL,		0 },
6845297697Santon 	};
6945297697Santon 	unsigned long *cover;
7045297697Santon 	int c, fd, i;
719257d67bSanton 	int error = 0;
720e2965f9Santon 	int mode = 0;
7345297697Santon 	int prereq = 0;
7445297697Santon 	int reexec = 0;
7545297697Santon 	int verbose = 0;
7645297697Santon 
7745297697Santon 	self = argv[0];
7845297697Santon 
790e2965f9Santon 	while ((c = getopt(argc, argv, "Em:pv")) != -1)
8045297697Santon 		switch (c) {
8145297697Santon 		case 'E':
8245297697Santon 			reexec = 1;
8345297697Santon 			break;
840e2965f9Santon 		case 'm':
850e2965f9Santon 			if (strcmp(optarg, "pc") == 0)
860e2965f9Santon 				mode = KCOV_MODE_TRACE_PC;
87*a4101078Santon 			else if (strcmp(optarg, "cmp") == 0)
88*a4101078Santon 				mode = KCOV_MODE_TRACE_CMP;
890e2965f9Santon 			else
900e2965f9Santon 				errx(1, "unknown mode %s", optarg);
910e2965f9Santon 			break;
9245297697Santon 		case 'p':
9345297697Santon 			prereq = 1;
9445297697Santon 			break;
9545297697Santon 		case 'v':
9645297697Santon 			verbose = 1;
9745297697Santon 			break;
9845297697Santon 		default:
9945297697Santon 			usage();
10045297697Santon 		}
10145297697Santon 	argc -= optind;
10245297697Santon 	argv += optind;
10345297697Santon 
10445297697Santon 	if (prereq) {
10545297697Santon 		fd = kcov_open();
10645297697Santon 		close(fd);
10745297697Santon 		return 0;
10845297697Santon 	}
10945297697Santon 
11045297697Santon 	if (reexec) {
11145297697Santon 		do_syscall();
11245297697Santon 		return 0;
11345297697Santon 	}
11445297697Santon 
1150e2965f9Santon 	if (mode == 0 || argc != 1)
1169257d67bSanton 		usage();
1179257d67bSanton 	for (i = 0; tests[i].name != NULL; i++)
1189257d67bSanton 		if (strcmp(argv[0], tests[i].name) == 0)
1199257d67bSanton 			break;
1209257d67bSanton 	if (tests[i].name == NULL)
1219257d67bSanton 		errx(1, "%s: no such test", argv[0]);
1229257d67bSanton 
12345297697Santon 	fd = kcov_open();
12445297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1)
12545297697Santon 		err(1, "ioctl: KIOSETBUFSIZE");
12645297697Santon 	cover = mmap(NULL, bufsize * sizeof(unsigned long),
12745297697Santon 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
12845297697Santon 	if (cover == MAP_FAILED)
12945297697Santon 		err(1, "mmap");
13045297697Santon 
13145297697Santon 	*cover = 0;
1320e2965f9Santon 	error = tests[i].fn(fd, mode);
13345297697Santon 	if (verbose)
1343bb8297cSanton 		dump(cover, mode);
1353bb8297cSanton 	if (check_coverage(cover, mode, bufsize, tests[i].coverage))
1369257d67bSanton 		error = 1;
13745297697Santon 
13845297697Santon 	if (munmap(cover, bufsize * sizeof(unsigned long)) == -1)
13945297697Santon 		err(1, "munmap");
14045297697Santon 	close(fd);
14145297697Santon 
1429257d67bSanton 	return error;
14345297697Santon }
14445297697Santon 
14545297697Santon static __dead void
14645297697Santon usage(void)
14745297697Santon {
1480e2965f9Santon 	fprintf(stderr, "usage: kcov [-Epv] -t mode test\n");
14945297697Santon 	exit(1);
15045297697Santon }
15145297697Santon 
15245297697Santon static void
15345297697Santon do_syscall(void)
15445297697Santon {
15545297697Santon 	getpid();
15645297697Santon }
15745297697Santon 
1583bb8297cSanton static int
1593bb8297cSanton check_coverage(const unsigned long *cover, int mode, unsigned long maxsize,
1603bb8297cSanton     int nonzero)
1613bb8297cSanton {
162*a4101078Santon 	unsigned long arg1, arg2, exp, i, pc, type;
1633bb8297cSanton 	int error = 0;
1643bb8297cSanton 
1653bb8297cSanton 	if (nonzero && cover[0] == 0) {
1663bb8297cSanton 		warnx("coverage empty (count=0)\n");
1673bb8297cSanton 		return 1;
1683bb8297cSanton 	} else if (!nonzero && cover[0] != 0) {
1693bb8297cSanton 		warnx("coverage not empty (count=%lu)\n", *cover);
1703bb8297cSanton 		return 1;
1713bb8297cSanton 	} else if (cover[0] >= maxsize) {
1723bb8297cSanton 		warnx("coverage overflow (count=%lu, max=%lu)\n", *cover, maxsize);
1733bb8297cSanton 		return 1;
1743bb8297cSanton 	}
1753bb8297cSanton 
176*a4101078Santon 	if (mode == KCOV_MODE_TRACE_CMP) {
177*a4101078Santon 		if (*cover * 4 >= maxsize) {
178*a4101078Santon 			warnx("coverage cmp overflow (count=%lu, max=%lu)\n",
179*a4101078Santon 			    *cover * 4, maxsize);
180*a4101078Santon 			return 1;
181*a4101078Santon 		}
182*a4101078Santon 
183*a4101078Santon 		for (i = 0; i < cover[0]; i++) {
184*a4101078Santon 			type = cover[i * 4 + 1];
185*a4101078Santon 			arg1 = cover[i * 4 + 2];
186*a4101078Santon 			arg2 = cover[i * 4 + 3];
187*a4101078Santon 			pc = cover[i * 4 + 4];
188*a4101078Santon 
189*a4101078Santon 			exp = type >> 1;
190*a4101078Santon 			if (exp <= 3)
191*a4101078Santon 				continue;
192*a4101078Santon 
193*a4101078Santon 			warnx("coverage cmp invalid size (i=%lu, exp=%lx, "
194*a4101078Santon 			    "const=%ld, arg1=%lu, arg2=%lu, pc=%p)\n",
195*a4101078Santon 			    i, exp, type & 0x1, arg1, arg2, (void *)pc);
196*a4101078Santon 			error = 1;
197*a4101078Santon 		}
198*a4101078Santon 	}
199*a4101078Santon 
2003bb8297cSanton 	return error;
2013bb8297cSanton }
2023bb8297cSanton 
20345297697Santon static void
2043bb8297cSanton dump(const unsigned long *cover, int mode)
20545297697Santon {
20645297697Santon 	unsigned long i;
2073bb8297cSanton 	int stride = 1;
20845297697Santon 
209*a4101078Santon 	if (mode == KCOV_MODE_TRACE_CMP)
210*a4101078Santon 		stride = 4;
211*a4101078Santon 
21245297697Santon 	for (i = 0; i < cover[0]; i++)
2133bb8297cSanton 		printf("%p\n", (void *)cover[i * stride + 1]);
21445297697Santon }
21545297697Santon 
21645297697Santon static int
21745297697Santon kcov_open(void)
21845297697Santon {
21945297697Santon 	int fd;
22045297697Santon 
22145297697Santon 	fd = open("/dev/kcov", O_RDWR);
22245297697Santon 	if (fd == -1)
22345297697Santon 		err(1, "open: /dev/kcov");
22445297697Santon 	return fd;
22545297697Santon }
22645297697Santon 
22745297697Santon static void
2280e2965f9Santon kcov_enable(int fd, int mode)
22945297697Santon {
2300e2965f9Santon 	if (ioctl(fd, KIOENABLE, &mode) == -1)
23145297697Santon 		err(1, "ioctl: KIOENABLE");
23245297697Santon }
23345297697Santon 
23445297697Santon static void
23545297697Santon kcov_disable(int fd)
23645297697Santon {
23745297697Santon 	if (ioctl(fd, KIODISABLE) == -1)
23845297697Santon 		err(1, "ioctl: KIODISABLE");
23945297697Santon }
24045297697Santon 
24145297697Santon /*
24245297697Santon  * Close before mmap.
24345297697Santon  */
24445297697Santon static int
2450e2965f9Santon test_close(int oldfd, int mode)
24645297697Santon {
24745297697Santon 	int fd;
24845297697Santon 
24945297697Santon 	fd = kcov_open();
25045297697Santon 	close(fd);
25145297697Santon 	return 0;
25245297697Santon }
25345297697Santon 
25445297697Santon /*
25545297697Santon  * Coverage of current thread.
25645297697Santon  */
25745297697Santon static int
2580e2965f9Santon test_coverage(int fd, int mode)
25945297697Santon {
2600e2965f9Santon 	kcov_enable(fd, mode);
26145297697Santon 	do_syscall();
26245297697Santon 	kcov_disable(fd);
26345297697Santon 	return 0;
26445297697Santon }
26545297697Santon 
266aad1a0fbSanton static void *
267aad1a0fbSanton closer(void *arg)
268aad1a0fbSanton {
269aad1a0fbSanton 	int fd = *((int *)arg);
270aad1a0fbSanton 
271aad1a0fbSanton 	close(fd);
272aad1a0fbSanton 	return NULL;
273aad1a0fbSanton }
274aad1a0fbSanton 
275aad1a0fbSanton /*
276aad1a0fbSanton  * Close kcov descriptor in another thread during tracing.
277aad1a0fbSanton  */
278aad1a0fbSanton static int
2790e2965f9Santon test_dying(int fd, int mode)
280aad1a0fbSanton {
281aad1a0fbSanton 	pthread_t th;
282aad1a0fbSanton 	int error;
283aad1a0fbSanton 
2840e2965f9Santon 	kcov_enable(fd, mode);
285aad1a0fbSanton 
286aad1a0fbSanton 	if ((error = pthread_create(&th, NULL, closer, &fd)))
287aad1a0fbSanton 		errc(1, error, "pthread_create");
288aad1a0fbSanton 	if ((error = pthread_join(th, NULL)))
289aad1a0fbSanton 		errc(1, error, "pthread_join");
290aad1a0fbSanton 
291aad1a0fbSanton 	if (close(fd) == -1) {
292aad1a0fbSanton 		if (errno != EBADF)
293aad1a0fbSanton 			err(1, "close");
294aad1a0fbSanton 	} else {
295aad1a0fbSanton 		warnx("expected kcov descriptor to be closed");
296aad1a0fbSanton 		return 1;
297aad1a0fbSanton 	}
298aad1a0fbSanton 
299aad1a0fbSanton 	return 0;
300aad1a0fbSanton }
301aad1a0fbSanton 
30245297697Santon /*
30345297697Santon  * Coverage of thread after exec.
30445297697Santon  */
30545297697Santon static int
3060e2965f9Santon test_exec(int fd, int mode)
30745297697Santon {
30845297697Santon 	pid_t pid;
30945297697Santon 	int status;
31045297697Santon 
31145297697Santon 	pid = fork();
31245297697Santon 	if (pid == -1)
31345297697Santon 		err(1, "fork");
31445297697Santon 	if (pid == 0) {
3150e2965f9Santon 		kcov_enable(fd, mode);
31645297697Santon 		execlp(self, self, "-E", NULL);
31745297697Santon 		_exit(1);
31845297697Santon 	}
31945297697Santon 
32045297697Santon 	if (waitpid(pid, &status, 0) == -1)
32145297697Santon 		err(1, "waitpid");
32245297697Santon 	if (WIFSIGNALED(status)) {
32345297697Santon 		warnx("terminated by signal (%d)", WTERMSIG(status));
32445297697Santon 		return 1;
32545297697Santon 	} else if (WEXITSTATUS(status) != 0) {
32645297697Santon 		warnx("non-zero exit (%d)", WEXITSTATUS(status));
32745297697Santon 		return 1;
32845297697Santon 	}
32945297697Santon 
33045297697Santon 	/* Upon exit, the kcov descriptor must be reusable again. */
3310e2965f9Santon 	kcov_enable(fd, mode);
33245297697Santon 	kcov_disable(fd);
33345297697Santon 
33445297697Santon 	return 0;
33545297697Santon }
33645297697Santon 
33745297697Santon /*
33845297697Santon  * Coverage of thread after fork.
33945297697Santon  */
34045297697Santon static int
3410e2965f9Santon test_fork(int fd, int mode)
34245297697Santon {
34345297697Santon 	pid_t pid;
34445297697Santon 	int status;
34545297697Santon 
34645297697Santon 	pid = fork();
34745297697Santon 	if (pid == -1)
34845297697Santon 		err(1, "fork");
34945297697Santon 	if (pid == 0) {
3500e2965f9Santon 		kcov_enable(fd, mode);
35145297697Santon 		do_syscall();
35245297697Santon 		_exit(0);
35345297697Santon 	}
35445297697Santon 
35545297697Santon 	if (waitpid(pid, &status, 0) == -1)
35645297697Santon 		err(1, "waitpid");
35745297697Santon 	if (WIFSIGNALED(status)) {
35845297697Santon 		warnx("terminated by signal (%d)", WTERMSIG(status));
35945297697Santon 		return 1;
36045297697Santon 	} else if (WEXITSTATUS(status) != 0) {
36145297697Santon 		warnx("non-zero exit (%d)", WEXITSTATUS(status));
36245297697Santon 		return 1;
36345297697Santon 	}
36445297697Santon 
36545297697Santon 	/* Upon exit, the kcov descriptor must be reusable again. */
3660e2965f9Santon 	kcov_enable(fd, mode);
36745297697Santon 	kcov_disable(fd);
36845297697Santon 
36945297697Santon 	return 0;
37045297697Santon }
37145297697Santon 
37245297697Santon /*
373e3865369Santon  * Open /dev/kcov more than once.
37445297697Santon  */
37545297697Santon static int
3760e2965f9Santon test_open(int oldfd, int mode)
377e3865369Santon {
378e3865369Santon 	unsigned long *cover;
379e3865369Santon 	int fd;
3803bb8297cSanton 	int error = 0;
381e3865369Santon 
382e3865369Santon 	fd = kcov_open();
383e3865369Santon 	if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1)
384e3865369Santon 		err(1, "ioctl: KIOSETBUFSIZE");
385e3865369Santon 	cover = mmap(NULL, bufsize * sizeof(unsigned long),
386e3865369Santon 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
387e3865369Santon 	if (cover == MAP_FAILED)
388e3865369Santon 		err(1, "mmap");
389e3865369Santon 
3900e2965f9Santon 	kcov_enable(fd, mode);
391e3865369Santon 	do_syscall();
392e3865369Santon 	kcov_disable(fd);
393e3865369Santon 
3943bb8297cSanton 	error = check_coverage(cover, mode, bufsize, 1);
3953bb8297cSanton 
396e3865369Santon 	if (munmap(cover, bufsize * sizeof(unsigned long)))
397e3865369Santon 		err(1, "munmap");
398e3865369Santon 	close(fd);
3993bb8297cSanton 
4003bb8297cSanton 	return error;
401e3865369Santon }
402e3865369Santon 
403e3865369Santon /*
404e3865369Santon  * State transitions.
405e3865369Santon  */
406e3865369Santon static int
4070e2965f9Santon test_state(int fd, int mode)
40845297697Santon {
4090e2965f9Santon 	if (ioctl(fd, KIOENABLE, &mode) == -1) {
4100e2965f9Santon 		warn("KIOSETBUFSIZE -> KIOENABLE");
41145297697Santon 		return 1;
41245297697Santon 	}
41345297697Santon 	if (ioctl(fd, KIODISABLE) == -1) {
4140e2965f9Santon 		warn("KIOENABLE -> KIODISABLE");
41545297697Santon 		return 1;
41645297697Santon 	}
41745297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) {
41845297697Santon 		warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE");
41945297697Santon 		return 1;
42045297697Santon 	}
42145297697Santon 	if (ioctl(fd, KIODISABLE) != -1) {
42245297697Santon 		warnx("KIOSETBUFSIZE -> KIODISABLE");
42345297697Santon 		return 1;
42445297697Santon 	}
42545297697Santon 
4260e2965f9Santon 	kcov_enable(fd, mode);
4270e2965f9Santon 	if (ioctl(fd, KIOENABLE, &mode) != -1) {
42845297697Santon 		warnx("KIOENABLE -> KIOENABLE");
42945297697Santon 		return 1;
43045297697Santon 	}
43145297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) {
43245297697Santon 		warnx("KIOENABLE -> KIOSETBUFSIZE");
43345297697Santon 		return 1;
43445297697Santon 	}
43545297697Santon 	kcov_disable(fd);
43645297697Santon 
43745297697Santon 	return 0;
43845297697Santon }
439