xref: /openbsd/regress/sys/dev/kcov/kcov.c (revision 45297697)
1*45297697Santon /*	$OpenBSD: kcov.c,v 1.1 2018/08/26 08:12:09 anton Exp $	*/
2*45297697Santon 
3*45297697Santon /*
4*45297697Santon  * Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
5*45297697Santon  *
6*45297697Santon  * Permission to use, copy, modify, and distribute this software for any
7*45297697Santon  * purpose with or without fee is hereby granted, provided that the above
8*45297697Santon  * copyright notice and this permission notice appear in all copies.
9*45297697Santon  *
10*45297697Santon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11*45297697Santon  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*45297697Santon  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13*45297697Santon  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*45297697Santon  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*45297697Santon  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*45297697Santon  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*45297697Santon  */
18*45297697Santon 
19*45297697Santon #include <sys/ioctl.h>
20*45297697Santon #include <sys/kcov.h>
21*45297697Santon #include <sys/mman.h>
22*45297697Santon #include <sys/wait.h>
23*45297697Santon 
24*45297697Santon #include <err.h>
25*45297697Santon #include <errno.h>
26*45297697Santon #include <fcntl.h>
27*45297697Santon #include <stdio.h>
28*45297697Santon #include <stdlib.h>
29*45297697Santon #include <unistd.h>
30*45297697Santon 
31*45297697Santon static int test_close(int);
32*45297697Santon static int test_coverage(int);
33*45297697Santon static int test_exec(int);
34*45297697Santon static int test_fork(int);
35*45297697Santon static int test_mode(int);
36*45297697Santon static int test_open(int);
37*45297697Santon 
38*45297697Santon static void do_syscall(void);
39*45297697Santon static void dump(const unsigned long *);
40*45297697Santon static void kcov_disable(int);
41*45297697Santon static void kcov_enable(int);
42*45297697Santon static int kcov_open(void);
43*45297697Santon static __dead void usage(void);
44*45297697Santon 
45*45297697Santon static const char *self;
46*45297697Santon static unsigned long bufsize = 256 << 10;
47*45297697Santon 
48*45297697Santon int
49*45297697Santon main(int argc, char *argv[])
50*45297697Santon {
51*45297697Santon 	struct {
52*45297697Santon 		const char *name;
53*45297697Santon 		int (*fn)(int);
54*45297697Santon 		int coverage;		/* test must produce coverage */
55*45297697Santon 	} tests[] = {
56*45297697Santon 		{ "coverage",	test_coverage,	1 },
57*45297697Santon 		{ "fork",	test_fork,	1 },
58*45297697Santon 		{ "exec",	test_exec,	1 },
59*45297697Santon 		{ "mode",	test_mode,	1 },
60*45297697Santon 		{ "open",	test_open,	0 },
61*45297697Santon 		{ "close",	test_close,	0 },
62*45297697Santon 		{ NULL,		NULL,		0 },
63*45297697Santon 	};
64*45297697Santon 	unsigned long *cover;
65*45297697Santon 	int c, fd, i;
66*45297697Santon 	int nfail = 0;
67*45297697Santon 	int prereq = 0;
68*45297697Santon 	int reexec = 0;
69*45297697Santon 	int verbose = 0;
70*45297697Santon 
71*45297697Santon 	self = argv[0];
72*45297697Santon 
73*45297697Santon 	while ((c = getopt(argc, argv, "Epv")) != -1)
74*45297697Santon 		switch (c) {
75*45297697Santon 		case 'E':
76*45297697Santon 			reexec = 1;
77*45297697Santon 			break;
78*45297697Santon 		case 'p':
79*45297697Santon 			prereq = 1;
80*45297697Santon 			break;
81*45297697Santon 		case 'v':
82*45297697Santon 			verbose = 1;
83*45297697Santon 			break;
84*45297697Santon 		default:
85*45297697Santon 			usage();
86*45297697Santon 		}
87*45297697Santon 	argc -= optind;
88*45297697Santon 	argv += optind;
89*45297697Santon 	if (argc > 0)
90*45297697Santon 		usage();
91*45297697Santon 
92*45297697Santon 	if (prereq) {
93*45297697Santon 		fd = kcov_open();
94*45297697Santon 		close(fd);
95*45297697Santon 		return 0;
96*45297697Santon 	}
97*45297697Santon 
98*45297697Santon 	if (reexec) {
99*45297697Santon 		do_syscall();
100*45297697Santon 		return 0;
101*45297697Santon 	}
102*45297697Santon 
103*45297697Santon 	fd = kcov_open();
104*45297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1)
105*45297697Santon 		err(1, "ioctl: KIOSETBUFSIZE");
106*45297697Santon 	cover = mmap(NULL, bufsize * sizeof(unsigned long),
107*45297697Santon 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
108*45297697Santon 	if (cover == MAP_FAILED)
109*45297697Santon 		err(1, "mmap");
110*45297697Santon 
111*45297697Santon 	for (i = 0; tests[i].name != NULL; i++) {
112*45297697Santon 		printf("===> %s\n", tests[i].name);
113*45297697Santon 		*cover = 0;
114*45297697Santon 		nfail += tests[i].fn(fd);
115*45297697Santon 		if (verbose)
116*45297697Santon 			dump(cover);
117*45297697Santon 		if (tests[i].coverage && *cover == 0) {
118*45297697Santon 			warnx("coverage empty (count=%lu, fd=%d)\n",
119*45297697Santon 			    *cover, fd);
120*45297697Santon 			nfail++;
121*45297697Santon 		} else if (!tests[i].coverage && *cover != 0) {
122*45297697Santon 			warnx("coverage is not empty (count=%lu, fd=%d)\n",
123*45297697Santon 			    *cover, fd);
124*45297697Santon 			nfail++;
125*45297697Santon 		}
126*45297697Santon 	}
127*45297697Santon 
128*45297697Santon 	if (munmap(cover, bufsize * sizeof(unsigned long)) == -1)
129*45297697Santon 		err(1, "munmap");
130*45297697Santon 	close(fd);
131*45297697Santon 
132*45297697Santon 	if (nfail > 0)
133*45297697Santon 		return 1;
134*45297697Santon 	return 0;
135*45297697Santon }
136*45297697Santon 
137*45297697Santon static __dead void
138*45297697Santon usage(void)
139*45297697Santon {
140*45297697Santon 	fprintf(stderr, "usage: kcov [-Epv]\n");
141*45297697Santon 	exit(1);
142*45297697Santon }
143*45297697Santon 
144*45297697Santon static void
145*45297697Santon do_syscall(void)
146*45297697Santon {
147*45297697Santon 	getpid();
148*45297697Santon }
149*45297697Santon 
150*45297697Santon static void
151*45297697Santon dump(const unsigned long *cover)
152*45297697Santon {
153*45297697Santon 	unsigned long i;
154*45297697Santon 
155*45297697Santon 	for (i = 0; i < cover[0]; i++)
156*45297697Santon 		printf("%lu/%lu: %p\n", i + 1, cover[0], (void *)cover[i + 1]);
157*45297697Santon }
158*45297697Santon 
159*45297697Santon static int
160*45297697Santon kcov_open(void)
161*45297697Santon {
162*45297697Santon 	int fd;
163*45297697Santon 
164*45297697Santon 	fd = open("/dev/kcov", O_RDWR);
165*45297697Santon 	if (fd == -1)
166*45297697Santon 		err(1, "open: /dev/kcov");
167*45297697Santon 	return fd;
168*45297697Santon }
169*45297697Santon 
170*45297697Santon static void
171*45297697Santon kcov_enable(int fd)
172*45297697Santon {
173*45297697Santon 	if (ioctl(fd, KIOENABLE) == -1)
174*45297697Santon 		err(1, "ioctl: KIOENABLE");
175*45297697Santon }
176*45297697Santon 
177*45297697Santon static void
178*45297697Santon kcov_disable(int fd)
179*45297697Santon {
180*45297697Santon 	if (ioctl(fd, KIODISABLE) == -1)
181*45297697Santon 		err(1, "ioctl: KIODISABLE");
182*45297697Santon }
183*45297697Santon 
184*45297697Santon /*
185*45297697Santon  * Close before mmap.
186*45297697Santon  */
187*45297697Santon static int
188*45297697Santon test_close(int oldfd)
189*45297697Santon {
190*45297697Santon 	int fd;
191*45297697Santon 
192*45297697Santon 	fd = kcov_open();
193*45297697Santon 	close(fd);
194*45297697Santon 	return 0;
195*45297697Santon }
196*45297697Santon 
197*45297697Santon /*
198*45297697Santon  * Coverage of current thread.
199*45297697Santon  */
200*45297697Santon static int
201*45297697Santon test_coverage(int fd)
202*45297697Santon {
203*45297697Santon 	kcov_enable(fd);
204*45297697Santon 	do_syscall();
205*45297697Santon 	kcov_disable(fd);
206*45297697Santon 	return 0;
207*45297697Santon }
208*45297697Santon 
209*45297697Santon /*
210*45297697Santon  * Coverage of thread after exec.
211*45297697Santon  */
212*45297697Santon static int
213*45297697Santon test_exec(int fd)
214*45297697Santon {
215*45297697Santon 	pid_t pid;
216*45297697Santon 	int status;
217*45297697Santon 
218*45297697Santon 	pid = fork();
219*45297697Santon 	if (pid == -1)
220*45297697Santon 		err(1, "fork");
221*45297697Santon 	if (pid == 0) {
222*45297697Santon 		kcov_enable(fd);
223*45297697Santon 		execlp(self, self, "-E", NULL);
224*45297697Santon 		_exit(1);
225*45297697Santon 	}
226*45297697Santon 
227*45297697Santon 	if (waitpid(pid, &status, 0) == -1)
228*45297697Santon 		err(1, "waitpid");
229*45297697Santon 	if (WIFSIGNALED(status)) {
230*45297697Santon 		warnx("terminated by signal (%d)", WTERMSIG(status));
231*45297697Santon 		return 1;
232*45297697Santon 	} else if (WEXITSTATUS(status) != 0) {
233*45297697Santon 		warnx("non-zero exit (%d)", WEXITSTATUS(status));
234*45297697Santon 		return 1;
235*45297697Santon 	}
236*45297697Santon 
237*45297697Santon 	/* Upon exit, the kcov descriptor must be reusable again. */
238*45297697Santon 	kcov_enable(fd);
239*45297697Santon 	kcov_disable(fd);
240*45297697Santon 
241*45297697Santon 	return 0;
242*45297697Santon }
243*45297697Santon 
244*45297697Santon /*
245*45297697Santon  * Coverage of thread after fork.
246*45297697Santon  */
247*45297697Santon static int
248*45297697Santon test_fork(int fd)
249*45297697Santon {
250*45297697Santon 	pid_t pid;
251*45297697Santon 	int status;
252*45297697Santon 
253*45297697Santon 	pid = fork();
254*45297697Santon 	if (pid == -1)
255*45297697Santon 		err(1, "fork");
256*45297697Santon 	if (pid == 0) {
257*45297697Santon 		kcov_enable(fd);
258*45297697Santon 		do_syscall();
259*45297697Santon 		_exit(0);
260*45297697Santon 	}
261*45297697Santon 
262*45297697Santon 	if (waitpid(pid, &status, 0) == -1)
263*45297697Santon 		err(1, "waitpid");
264*45297697Santon 	if (WIFSIGNALED(status)) {
265*45297697Santon 		warnx("terminated by signal (%d)", WTERMSIG(status));
266*45297697Santon 		return 1;
267*45297697Santon 	} else if (WEXITSTATUS(status) != 0) {
268*45297697Santon 		warnx("non-zero exit (%d)", WEXITSTATUS(status));
269*45297697Santon 		return 1;
270*45297697Santon 	}
271*45297697Santon 
272*45297697Santon 	/* Upon exit, the kcov descriptor must be reusable again. */
273*45297697Santon 	kcov_enable(fd);
274*45297697Santon 	kcov_disable(fd);
275*45297697Santon 
276*45297697Santon 	return 0;
277*45297697Santon }
278*45297697Santon 
279*45297697Santon /*
280*45297697Santon  * Mode transitions.
281*45297697Santon  */
282*45297697Santon static int
283*45297697Santon test_mode(int fd)
284*45297697Santon {
285*45297697Santon 	if (ioctl(fd, KIOENABLE) == -1) {
286*45297697Santon 		warnx("KIOSETBUFSIZE -> KIOENABLE");
287*45297697Santon 		return 1;
288*45297697Santon 	}
289*45297697Santon 	if (ioctl(fd, KIODISABLE) == -1) {
290*45297697Santon 		warnx("KIOENABLE -> KIODISABLE");
291*45297697Santon 		return 1;
292*45297697Santon 	}
293*45297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) {
294*45297697Santon 		warnx("KIOSETBUFSIZE -> KIOSETBUFSIZE");
295*45297697Santon 		return 1;
296*45297697Santon 	}
297*45297697Santon 	if (ioctl(fd, KIODISABLE) != -1) {
298*45297697Santon 		warnx("KIOSETBUFSIZE -> KIODISABLE");
299*45297697Santon 		return 1;
300*45297697Santon 	}
301*45297697Santon 
302*45297697Santon 	kcov_enable(fd);
303*45297697Santon 	if (ioctl(fd, KIOENABLE) != -1) {
304*45297697Santon 		warnx("KIOENABLE -> KIOENABLE");
305*45297697Santon 		return 1;
306*45297697Santon 	}
307*45297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, 0) != -1) {
308*45297697Santon 		warnx("KIOENABLE -> KIOSETBUFSIZE");
309*45297697Santon 		return 1;
310*45297697Santon 	}
311*45297697Santon 	kcov_disable(fd);
312*45297697Santon 
313*45297697Santon 	return 0;
314*45297697Santon }
315*45297697Santon 
316*45297697Santon /*
317*45297697Santon  * Open /dev/kcov more than once.
318*45297697Santon  */
319*45297697Santon static int
320*45297697Santon test_open(int oldfd)
321*45297697Santon {
322*45297697Santon 	unsigned long *cover;
323*45297697Santon 	int fd;
324*45297697Santon 	int ret = 0;
325*45297697Santon 
326*45297697Santon 	fd = kcov_open();
327*45297697Santon 	if (ioctl(fd, KIOSETBUFSIZE, &bufsize) == -1)
328*45297697Santon 		err(1, "ioctl: KIOSETBUFSIZE");
329*45297697Santon 	cover = mmap(NULL, bufsize * sizeof(unsigned long),
330*45297697Santon 	    PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
331*45297697Santon 	if (cover == MAP_FAILED)
332*45297697Santon 		err(1, "mmap");
333*45297697Santon 
334*45297697Santon 	kcov_enable(fd);
335*45297697Santon 	do_syscall();
336*45297697Santon 	kcov_disable(fd);
337*45297697Santon 
338*45297697Santon 	if (*cover == 0) {
339*45297697Santon 		warnx("coverage empty (count=0, fd=%d)\n", fd);
340*45297697Santon 		ret = 1;
341*45297697Santon 	}
342*45297697Santon 	if (munmap(cover, bufsize * sizeof(unsigned long)))
343*45297697Santon 		err(1, "munmap");
344*45297697Santon 	close(fd);
345*45297697Santon 	return ret;
346*45297697Santon }
347