xref: /openbsd/regress/sys/dev/kcov/kcov.c (revision 0e2965f9)
1*0e2965f9Santon /*	$OpenBSD: kcov.c,v 1.5 2018/12/27 19:38:01 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*0e2965f9Santon static int test_close(int, int);
34*0e2965f9Santon static int test_coverage(int, int);
35*0e2965f9Santon static int test_dying(int, int);
36*0e2965f9Santon static int test_exec(int, int);
37*0e2965f9Santon static int test_fork(int, int);
38*0e2965f9Santon static int test_open(int, int);
39*0e2965f9Santon static int test_state(int, int);
4045297697Santon 
4145297697Santon static void do_syscall(void);
4245297697Santon static void dump(const unsigned long *);
4345297697Santon static void kcov_disable(int);
44*0e2965f9Santon static void kcov_enable(int, int);
4545297697Santon static int kcov_open(void);
4645297697Santon static __dead void usage(void);
4745297697Santon 
4845297697Santon static const char *self;
4945297697Santon static unsigned long bufsize = 256 << 10;
5045297697Santon 
5145297697Santon int
5245297697Santon main(int argc, char *argv[])
5345297697Santon {
5445297697Santon 	struct {
5545297697Santon 		const char *name;
56*0e2965f9Santon 		int (*fn)(int, int);
5745297697Santon 		int coverage;		/* test must produce coverage */
5845297697Santon 	} tests[] = {
5945297697Santon 		{ "close",	test_close,	0 },
60e3865369Santon 		{ "coverage",	test_coverage,	1 },
61aad1a0fbSanton 		{ "dying",	test_dying,	1 },
62e3865369Santon 		{ "exec",	test_exec,	1 },
63e3865369Santon 		{ "fork",	test_fork,	1 },
64e3865369Santon 		{ "open",	test_open,	0 },
65e3865369Santon 		{ "state",	test_state,	1 },
6645297697Santon 		{ NULL,		NULL,		0 },
6745297697Santon 	};
6845297697Santon 	unsigned long *cover;
6945297697Santon 	int c, fd, i;
709257d67bSanton 	int error = 0;
71*0e2965f9Santon 	int mode = 0;
7245297697Santon 	int prereq = 0;
7345297697Santon 	int reexec = 0;
7445297697Santon 	int verbose = 0;
7545297697Santon 
7645297697Santon 	self = argv[0];
7745297697Santon 
78*0e2965f9Santon 	while ((c = getopt(argc, argv, "Em:pv")) != -1)
7945297697Santon 		switch (c) {
8045297697Santon 		case 'E':
8145297697Santon 			reexec = 1;
8245297697Santon 			break;
83*0e2965f9Santon 		case 'm':
84*0e2965f9Santon 			if (strcmp(optarg, "pc") == 0)
85*0e2965f9Santon 				mode = KCOV_MODE_TRACE_PC;
86*0e2965f9Santon 			else
87*0e2965f9Santon 				errx(1, "unknown mode %s", optarg);
88*0e2965f9Santon 			break;
8945297697Santon 		case 'p':
9045297697Santon 			prereq = 1;
9145297697Santon 			break;
9245297697Santon 		case 'v':
9345297697Santon 			verbose = 1;
9445297697Santon 			break;
9545297697Santon 		default:
9645297697Santon 			usage();
9745297697Santon 		}
9845297697Santon 	argc -= optind;
9945297697Santon 	argv += optind;
10045297697Santon 
10145297697Santon 	if (prereq) {
10245297697Santon 		fd = kcov_open();
10345297697Santon 		close(fd);
10445297697Santon 		return 0;
10545297697Santon 	}
10645297697Santon 
10745297697Santon 	if (reexec) {
10845297697Santon 		do_syscall();
10945297697Santon 		return 0;
11045297697Santon 	}
11145297697Santon 
112*0e2965f9Santon 	if (mode == 0 || argc != 1)
1139257d67bSanton 		usage();
1149257d67bSanton 	for (i = 0; tests[i].name != NULL; i++)
1159257d67bSanton 		if (strcmp(argv[0], tests[i].name) == 0)
1169257d67bSanton 			break;
1179257d67bSanton 	if (tests[i].name == NULL)
1189257d67bSanton 		errx(1, "%s: no such test", argv[0]);
1199257d67bSanton 
12045297697Santon 	fd = kcov_open();
12145297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1)
12245297697Santon 		err(1, "ioctl: KIOSETBUFSIZE");
12345297697Santon 	cover = mmap(NULL, bufsize * sizeof(unsigned long),
12445297697Santon 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
12545297697Santon 	if (cover == MAP_FAILED)
12645297697Santon 		err(1, "mmap");
12745297697Santon 
12845297697Santon 	*cover = 0;
129*0e2965f9Santon 	error = tests[i].fn(fd, mode);
13045297697Santon 	if (verbose)
13145297697Santon 		dump(cover);
13245297697Santon 	if (tests[i].coverage && *cover == 0) {
1339257d67bSanton                 warnx("coverage empty (count=%lu, fd=%d)\n", *cover, fd);
1349257d67bSanton 		error = 1;
13545297697Santon 	} else if (!tests[i].coverage && *cover != 0) {
1369257d67bSanton                 warnx("coverage is not empty (count=%lu, fd=%d)\n", *cover, fd);
1379257d67bSanton 		error = 1;
13845297697Santon 	}
13945297697Santon 
14045297697Santon 	if (munmap(cover, bufsize * sizeof(unsigned long)) == -1)
14145297697Santon 		err(1, "munmap");
14245297697Santon 	close(fd);
14345297697Santon 
1449257d67bSanton 	return error;
14545297697Santon }
14645297697Santon 
14745297697Santon static __dead void
14845297697Santon usage(void)
14945297697Santon {
150*0e2965f9Santon 	fprintf(stderr, "usage: kcov [-Epv] -t mode test\n");
15145297697Santon 	exit(1);
15245297697Santon }
15345297697Santon 
15445297697Santon static void
15545297697Santon do_syscall(void)
15645297697Santon {
15745297697Santon 	getpid();
15845297697Santon }
15945297697Santon 
16045297697Santon static void
16145297697Santon dump(const unsigned long *cover)
16245297697Santon {
16345297697Santon 	unsigned long i;
16445297697Santon 
16545297697Santon 	for (i = 0; i < cover[0]; i++)
16645297697Santon 		printf("%lu/%lu: %p\n", i + 1, cover[0], (void *)cover[i + 1]);
16745297697Santon }
16845297697Santon 
16945297697Santon static int
17045297697Santon kcov_open(void)
17145297697Santon {
17245297697Santon 	int fd;
17345297697Santon 
17445297697Santon 	fd = open("/dev/kcov", O_RDWR);
17545297697Santon 	if (fd == -1)
17645297697Santon 		err(1, "open: /dev/kcov");
17745297697Santon 	return fd;
17845297697Santon }
17945297697Santon 
18045297697Santon static void
181*0e2965f9Santon kcov_enable(int fd, int mode)
18245297697Santon {
183*0e2965f9Santon 	if (ioctl(fd, KIOENABLE, &mode) == -1)
18445297697Santon 		err(1, "ioctl: KIOENABLE");
18545297697Santon }
18645297697Santon 
18745297697Santon static void
18845297697Santon kcov_disable(int fd)
18945297697Santon {
19045297697Santon 	if (ioctl(fd, KIODISABLE) == -1)
19145297697Santon 		err(1, "ioctl: KIODISABLE");
19245297697Santon }
19345297697Santon 
19445297697Santon /*
19545297697Santon  * Close before mmap.
19645297697Santon  */
19745297697Santon static int
198*0e2965f9Santon test_close(int oldfd, int mode)
19945297697Santon {
20045297697Santon 	int fd;
20145297697Santon 
20245297697Santon 	fd = kcov_open();
20345297697Santon 	close(fd);
20445297697Santon 	return 0;
20545297697Santon }
20645297697Santon 
20745297697Santon /*
20845297697Santon  * Coverage of current thread.
20945297697Santon  */
21045297697Santon static int
211*0e2965f9Santon test_coverage(int fd, int mode)
21245297697Santon {
213*0e2965f9Santon 	kcov_enable(fd, mode);
21445297697Santon 	do_syscall();
21545297697Santon 	kcov_disable(fd);
21645297697Santon 	return 0;
21745297697Santon }
21845297697Santon 
219aad1a0fbSanton static void *
220aad1a0fbSanton closer(void *arg)
221aad1a0fbSanton {
222aad1a0fbSanton 	int fd = *((int *)arg);
223aad1a0fbSanton 
224aad1a0fbSanton 	close(fd);
225aad1a0fbSanton 	return NULL;
226aad1a0fbSanton }
227aad1a0fbSanton 
228aad1a0fbSanton /*
229aad1a0fbSanton  * Close kcov descriptor in another thread during tracing.
230aad1a0fbSanton  */
231aad1a0fbSanton static int
232*0e2965f9Santon test_dying(int fd, int mode)
233aad1a0fbSanton {
234aad1a0fbSanton 	pthread_t th;
235aad1a0fbSanton 	int error;
236aad1a0fbSanton 
237*0e2965f9Santon 	kcov_enable(fd, mode);
238aad1a0fbSanton 
239aad1a0fbSanton 	if ((error = pthread_create(&th, NULL, closer, &fd)))
240aad1a0fbSanton 		errc(1, error, "pthread_create");
241aad1a0fbSanton 	if ((error = pthread_join(th, NULL)))
242aad1a0fbSanton 		errc(1, error, "pthread_join");
243aad1a0fbSanton 
244aad1a0fbSanton 	if (close(fd) == -1) {
245aad1a0fbSanton 		if (errno != EBADF)
246aad1a0fbSanton 			err(1, "close");
247aad1a0fbSanton 	} else {
248aad1a0fbSanton 		warnx("expected kcov descriptor to be closed");
249aad1a0fbSanton 		return 1;
250aad1a0fbSanton 	}
251aad1a0fbSanton 
252aad1a0fbSanton 	return 0;
253aad1a0fbSanton }
254aad1a0fbSanton 
25545297697Santon /*
25645297697Santon  * Coverage of thread after exec.
25745297697Santon  */
25845297697Santon static int
259*0e2965f9Santon test_exec(int fd, int mode)
26045297697Santon {
26145297697Santon 	pid_t pid;
26245297697Santon 	int status;
26345297697Santon 
26445297697Santon 	pid = fork();
26545297697Santon 	if (pid == -1)
26645297697Santon 		err(1, "fork");
26745297697Santon 	if (pid == 0) {
268*0e2965f9Santon 		kcov_enable(fd, mode);
26945297697Santon 		execlp(self, self, "-E", NULL);
27045297697Santon 		_exit(1);
27145297697Santon 	}
27245297697Santon 
27345297697Santon 	if (waitpid(pid, &status, 0) == -1)
27445297697Santon 		err(1, "waitpid");
27545297697Santon 	if (WIFSIGNALED(status)) {
27645297697Santon 		warnx("terminated by signal (%d)", WTERMSIG(status));
27745297697Santon 		return 1;
27845297697Santon 	} else if (WEXITSTATUS(status) != 0) {
27945297697Santon 		warnx("non-zero exit (%d)", WEXITSTATUS(status));
28045297697Santon 		return 1;
28145297697Santon 	}
28245297697Santon 
28345297697Santon 	/* Upon exit, the kcov descriptor must be reusable again. */
284*0e2965f9Santon 	kcov_enable(fd, mode);
28545297697Santon 	kcov_disable(fd);
28645297697Santon 
28745297697Santon 	return 0;
28845297697Santon }
28945297697Santon 
29045297697Santon /*
29145297697Santon  * Coverage of thread after fork.
29245297697Santon  */
29345297697Santon static int
294*0e2965f9Santon test_fork(int fd, int mode)
29545297697Santon {
29645297697Santon 	pid_t pid;
29745297697Santon 	int status;
29845297697Santon 
29945297697Santon 	pid = fork();
30045297697Santon 	if (pid == -1)
30145297697Santon 		err(1, "fork");
30245297697Santon 	if (pid == 0) {
303*0e2965f9Santon 		kcov_enable(fd, mode);
30445297697Santon 		do_syscall();
30545297697Santon 		_exit(0);
30645297697Santon 	}
30745297697Santon 
30845297697Santon 	if (waitpid(pid, &status, 0) == -1)
30945297697Santon 		err(1, "waitpid");
31045297697Santon 	if (WIFSIGNALED(status)) {
31145297697Santon 		warnx("terminated by signal (%d)", WTERMSIG(status));
31245297697Santon 		return 1;
31345297697Santon 	} else if (WEXITSTATUS(status) != 0) {
31445297697Santon 		warnx("non-zero exit (%d)", WEXITSTATUS(status));
31545297697Santon 		return 1;
31645297697Santon 	}
31745297697Santon 
31845297697Santon 	/* Upon exit, the kcov descriptor must be reusable again. */
319*0e2965f9Santon 	kcov_enable(fd, mode);
32045297697Santon 	kcov_disable(fd);
32145297697Santon 
32245297697Santon 	return 0;
32345297697Santon }
32445297697Santon 
32545297697Santon /*
326e3865369Santon  * Open /dev/kcov more than once.
32745297697Santon  */
32845297697Santon static int
329*0e2965f9Santon test_open(int oldfd, int mode)
330e3865369Santon {
331e3865369Santon 	unsigned long *cover;
332e3865369Santon 	int fd;
333e3865369Santon 	int ret = 0;
334e3865369Santon 
335e3865369Santon 	fd = kcov_open();
336e3865369Santon 	if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1)
337e3865369Santon 		err(1, "ioctl: KIOSETBUFSIZE");
338e3865369Santon 	cover = mmap(NULL, bufsize * sizeof(unsigned long),
339e3865369Santon 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
340e3865369Santon 	if (cover == MAP_FAILED)
341e3865369Santon 		err(1, "mmap");
342e3865369Santon 
343*0e2965f9Santon 	kcov_enable(fd, mode);
344e3865369Santon 	do_syscall();
345e3865369Santon 	kcov_disable(fd);
346e3865369Santon 
347e3865369Santon 	if (*cover == 0) {
348e3865369Santon 		warnx("coverage empty (count=0, fd=%d)\n", fd);
349e3865369Santon 		ret = 1;
350e3865369Santon 	}
351e3865369Santon 	if (munmap(cover, bufsize * sizeof(unsigned long)))
352e3865369Santon 		err(1, "munmap");
353e3865369Santon 	close(fd);
354e3865369Santon 	return ret;
355e3865369Santon }
356e3865369Santon 
357e3865369Santon /*
358e3865369Santon  * State transitions.
359e3865369Santon  */
360e3865369Santon static int
361*0e2965f9Santon test_state(int fd, int mode)
36245297697Santon {
363*0e2965f9Santon 	if (ioctl(fd, KIOENABLE, &mode) == -1) {
364*0e2965f9Santon 		warn("KIOSETBUFSIZE -> KIOENABLE");
36545297697Santon 		return 1;
36645297697Santon 	}
36745297697Santon 	if (ioctl(fd, KIODISABLE) == -1) {
368*0e2965f9Santon 		warn("KIOENABLE -> KIODISABLE");
36945297697Santon 		return 1;
37045297697Santon 	}
37145297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) {
37245297697Santon 		warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE");
37345297697Santon 		return 1;
37445297697Santon 	}
37545297697Santon 	if (ioctl(fd, KIODISABLE) != -1) {
37645297697Santon 		warnx("KIOSETBUFSIZE -> KIODISABLE");
37745297697Santon 		return 1;
37845297697Santon 	}
37945297697Santon 
380*0e2965f9Santon 	kcov_enable(fd, mode);
381*0e2965f9Santon 	if (ioctl(fd, KIOENABLE, &mode) != -1) {
38245297697Santon 		warnx("KIOENABLE -> KIOENABLE");
38345297697Santon 		return 1;
38445297697Santon 	}
38545297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) {
38645297697Santon 		warnx("KIOENABLE -> KIOSETBUFSIZE");
38745297697Santon 		return 1;
38845297697Santon 	}
38945297697Santon 	kcov_disable(fd);
39045297697Santon 
39145297697Santon 	return 0;
39245297697Santon }
393