1 /*-
2  * Copyright (c) 2003-2004, 2010 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Portions of this software were developed at the University of Cambridge
6  * Computer Laboratory with support from a grant from Google, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 #include <sys/types.h>
33 #include <sys/mman.h>
34 #include <sys/resource.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/wait.h>
39 
40 #include <assert.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <inttypes.h>
45 #include <limits.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 static struct timespec ts_start, ts_end;
53 static int alarm_timeout;
54 static volatile int alarm_fired;
55 
56 #define timespecsub(vvp, uvp)						\
57 	do {								\
58 		(vvp)->tv_sec -= (uvp)->tv_sec;				\
59 		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
60 		if ((vvp)->tv_nsec < 0) {				\
61 			(vvp)->tv_sec--;				\
62 			(vvp)->tv_nsec += 1000000000;			\
63 		}							\
64 	} while (0)
65 
66 static void
67 alarm_handler(int signum)
68 {
69 
70 	alarm_fired = 1;
71 }
72 
73 static void
74 benchmark_start(void)
75 {
76 	int error;
77 
78 	alarm_fired = 0;
79 	if (alarm_timeout) {
80 		signal(SIGALRM, alarm_handler);
81 		alarm(alarm_timeout);
82 	}
83 	error = clock_gettime(CLOCK_REALTIME, &ts_start);
84 	assert(error == 0);
85 }
86 
87 static void
88 benchmark_stop(void)
89 {
90 	int error;
91 
92 	error = clock_gettime(CLOCK_REALTIME, &ts_end);
93 	assert(error == 0);
94 }
95 
96 uintmax_t
97 test_getuid(uintmax_t num, uintmax_t int_arg, const char *path)
98 {
99 	uintmax_t i;
100 
101 	/*
102 	 * Thread-local data should require no locking if system
103 	 * call is MPSAFE.
104 	 */
105 	benchmark_start();
106 	for (i = 0; i < num; i++) {
107 		if (alarm_fired)
108 			break;
109 		getuid();
110 	}
111 	benchmark_stop();
112 	return (i);
113 }
114 
115 uintmax_t
116 test_getppid(uintmax_t num, uintmax_t int_arg, const char *path)
117 {
118 	uintmax_t i;
119 
120 	/*
121 	 * This is process-local, but can change, so will require a
122 	 * lock.
123 	 */
124 	benchmark_start();
125 	for (i = 0; i < num; i++) {
126 		if (alarm_fired)
127 			break;
128 		getppid();
129 	}
130 	benchmark_stop();
131 	return (i);
132 }
133 
134 uintmax_t
135 test_getresuid(uintmax_t num, uintmax_t int_arg, const char *path)
136 {
137 	uid_t ruid, euid, suid;
138 	uintmax_t i;
139 
140 	benchmark_start();
141 	for (i = 0; i < num; i++) {
142 		if (alarm_fired)
143 			break;
144 		(void)getresuid(&ruid, &euid, &suid);
145 	}
146 	benchmark_stop();
147 	return (i);
148 }
149 
150 uintmax_t
151 test_clock_gettime(uintmax_t num, uintmax_t int_arg, const char *path)
152 {
153 	struct timespec ts;
154 	uintmax_t i;
155 
156 	benchmark_start();
157 	for (i = 0; i < num; i++) {
158 		if (alarm_fired)
159 			break;
160 		(void)clock_gettime(CLOCK_REALTIME, &ts);
161 	}
162 	benchmark_stop();
163 	return (i);
164 }
165 
166 uintmax_t
167 test_gettimeofday(uintmax_t num, uintmax_t int_arg, const char *path)
168 {
169 	struct timeval tv;
170 	uintmax_t i;
171 
172 	benchmark_start();
173 	for (i = 0; i < num; i++) {
174 		if (alarm_fired)
175 			break;
176 		(void)gettimeofday(&tv, NULL);
177 	}
178 	benchmark_stop();
179 	return (i);
180 }
181 
182 uintmax_t
183 test_getpriority(uintmax_t num, uintmax_t int_arg, const char *path)
184 {
185 	uintmax_t i;
186 
187 	benchmark_start();
188 	for (i = 0; i < num; i++) {
189 		if (alarm_fired)
190 			break;
191 		(void)getpriority(PRIO_PROCESS, 0);
192 	}
193 	benchmark_stop();
194 	return (i);
195 }
196 
197 uintmax_t
198 test_pipe(uintmax_t num, uintmax_t int_arg, const char *path)
199 {
200 	int fd[2], i;
201 
202 	/*
203 	 * pipe creation is expensive, as it will allocate a new file
204 	 * descriptor, allocate a new pipe, hook it all up, and return.
205 	 * Destroying is also expensive, as we now have to free up
206 	 * the file descriptors and return the pipe.
207 	 */
208 	if (pipe(fd) < 0)
209 		err(-1, "test_pipe: pipe");
210 	close(fd[0]);
211 	close(fd[1]);
212 	benchmark_start();
213 	for (i = 0; i < num; i++) {
214 		if (alarm_fired)
215 			break;
216 		if (pipe(fd) == -1)
217 			err(-1, "test_pipe: pipe");
218 		close(fd[0]);
219 		close(fd[1]);
220 	}
221 	benchmark_stop();
222 	return (i);
223 }
224 
225 uintmax_t
226 test_select(uintmax_t num, uintmax_t int_arg, const char *path)
227 {
228 	fd_set readfds, writefds, exceptfds;
229 	struct timeval tv;
230 	uintmax_t i;
231 	int error;
232 
233 	FD_ZERO(&readfds);
234 	FD_ZERO(&writefds);
235 	FD_ZERO(&exceptfds);
236 
237 	tv.tv_sec = 0;
238 	tv.tv_usec = 0;
239 
240 	benchmark_start();
241 	for (i = 0; i < num; i++) {
242 		if (alarm_fired)
243 			break;
244 		(void)select(0, &readfds, &writefds, &exceptfds, &tv);
245 	}
246 	benchmark_stop();
247 	return (i);
248 }
249 
250 uintmax_t
251 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path)
252 {
253 	uintmax_t i;
254 	int so;
255 
256 	so = socket(int_arg, SOCK_STREAM, 0);
257 	if (so < 0)
258 		err(-1, "test_socket_stream: socket");
259 	close(so);
260 	benchmark_start();
261 	for (i = 0; i < num; i++) {
262 		if (alarm_fired)
263 			break;
264 		so = socket(int_arg, SOCK_STREAM, 0);
265 		if (so == -1)
266 			err(-1, "test_socket_stream: socket");
267 		close(so);
268 	}
269 	benchmark_stop();
270 	return (i);
271 }
272 
273 uintmax_t
274 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
275 {
276 	uintmax_t i;
277 	int so;
278 
279 	so = socket(int_arg, SOCK_DGRAM, 0);
280 	if (so < 0)
281 		err(-1, "test_socket_dgram: socket");
282 	close(so);
283 	benchmark_start();
284 	for (i = 0; i < num; i++) {
285 		if (alarm_fired)
286 			break;
287 		so = socket(int_arg, SOCK_DGRAM, 0);
288 		if (so == -1)
289 			err(-1, "test_socket_dgram: socket");
290 		close(so);
291 	}
292 	benchmark_stop();
293 	return (i);
294 }
295 
296 uintmax_t
297 test_socketpair_stream(uintmax_t num, uintmax_t int_arg, const char *path)
298 {
299 	uintmax_t i;
300 	int so[2];
301 
302 	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
303 		err(-1, "test_socketpair_stream: socketpair");
304 	close(so[0]);
305 	close(so[1]);
306 	benchmark_start();
307 	for (i = 0; i < num; i++) {
308 		if (alarm_fired)
309 			break;
310 		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
311 			err(-1, "test_socketpair_stream: socketpair");
312 		close(so[0]);
313 		close(so[1]);
314 	}
315 	benchmark_stop();
316 	return (i);
317 }
318 
319 uintmax_t
320 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
321 {
322 	uintmax_t i;
323 	int so[2];
324 
325 	if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
326 		err(-1, "test_socketpair_dgram: socketpair");
327 	close(so[0]);
328 	close(so[1]);
329 	benchmark_start();
330 	for (i = 0; i < num; i++) {
331 		if (alarm_fired)
332 			break;
333 		if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
334 			err(-1, "test_socketpair_dgram: socketpair");
335 		close(so[0]);
336 		close(so[1]);
337 	}
338 	benchmark_stop();
339 	return (i);
340 }
341 
342 uintmax_t
343 test_access(uintmax_t num, uintmax_t int_arg, const char *path)
344 {
345 	uintmax_t i;
346 	int fd;
347 
348 	fd = access(path, O_RDONLY);
349 	if (fd < 0)
350 		err(-1, "test_access: %s", path);
351 	close(fd);
352 
353 	benchmark_start();
354 	for (i = 0; i < num; i++) {
355 		if (alarm_fired)
356 			break;
357 		access(path, O_RDONLY);
358 		close(fd);
359 	}
360 	benchmark_stop();
361 	return (i);
362 }
363 
364 uintmax_t
365 test_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path)
366 {
367 	uintmax_t i;
368 	int fd;
369 
370 	(void)unlink(path);
371 	fd = open(path, O_RDWR | O_CREAT, 0600);
372 	if (fd < 0)
373 		err(-1, "test_create_unlink: create: %s", path);
374 	close(fd);
375 	if (unlink(path) < 0)
376 		err(-1, "test_create_unlink: unlink: %s", path);
377 	benchmark_start();
378 	for (i = 0; i < num; i++) {
379 		if (alarm_fired)
380 			break;
381 		fd = open(path, O_RDWR | O_CREAT, 0600);
382 		if (fd < 0)
383 			err(-1, "test_create_unlink: create: %s", path);
384 		close(fd);
385 		if (unlink(path) < 0)
386 			err(-1, "test_create_unlink: unlink: %s", path);
387 	}
388 	benchmark_stop();
389 	return (i);
390 }
391 
392 uintmax_t
393 test_open_close(uintmax_t num, uintmax_t int_arg, const char *path)
394 {
395 	uintmax_t i;
396 	int fd;
397 
398 	fd = open(path, O_RDONLY);
399 	if (fd < 0)
400 		err(-1, "test_open_close: %s", path);
401 	close(fd);
402 
403 	benchmark_start();
404 	for (i = 0; i < num; i++) {
405 		if (alarm_fired)
406 			break;
407 		fd = open(path, O_RDONLY);
408 		if (fd < 0)
409 			err(-1, "test_open_close: %s", path);
410 		close(fd);
411 	}
412 	benchmark_stop();
413 	return (i);
414 }
415 
416 uintmax_t
417 test_bad_open(uintmax_t num, uintmax_t int_arg, const char *path)
418 {
419 	uintmax_t i;
420 
421 	benchmark_start();
422 	for (i = 0; i < num; i++) {
423 		if (alarm_fired)
424 			break;
425 		open("", O_RDONLY);
426 	}
427 	benchmark_stop();
428 	return (i);
429 }
430 
431 uintmax_t
432 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
433 {
434 	char buf[int_arg];
435 	uintmax_t i;
436 	int fd;
437 
438 	fd = open(path, O_RDONLY);
439 	if (fd < 0)
440 		err(-1, "test_open_read: %s", path);
441 	(void)pread(fd, buf, int_arg, 0);
442 
443 	benchmark_start();
444 	for (i = 0; i < num; i++) {
445 		if (alarm_fired)
446 			break;
447 		(void)pread(fd, buf, int_arg, 0);
448 	}
449 	benchmark_stop();
450 	close(fd);
451 	return (i);
452 }
453 
454 uintmax_t
455 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
456 {
457 	char buf[int_arg];
458 	uintmax_t i;
459 	int fd;
460 
461 	fd = open(path, O_RDONLY);
462 	if (fd < 0)
463 		err(-1, "test_open_read_close: %s", path);
464 	(void)read(fd, buf, int_arg);
465 	close(fd);
466 
467 	benchmark_start();
468 	for (i = 0; i < num; i++) {
469 		if (alarm_fired)
470 			break;
471 		fd = open(path, O_RDONLY);
472 		if (fd < 0)
473 			err(-1, "test_open_read_close: %s", path);
474 		(void)read(fd, buf, int_arg);
475 		close(fd);
476 	}
477 	benchmark_stop();
478 	return (i);
479 }
480 
481 uintmax_t
482 test_dup(uintmax_t num, uintmax_t int_arg, const char *path)
483 {
484 	int fd, i, shmfd;
485 
486 	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
487 	if (shmfd < 0)
488 		err(-1, "test_dup: shm_open");
489 	fd = dup(shmfd);
490 	if (fd >= 0)
491 		close(fd);
492 	benchmark_start();
493 	for (i = 0; i < num; i++) {
494 		if (alarm_fired)
495 			break;
496 		fd = dup(shmfd);
497 		if (fd >= 0)
498 			close(fd);
499 	}
500 	benchmark_stop();
501 	close(shmfd);
502 	return (i);
503 }
504 
505 uintmax_t
506 test_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
507 {
508 	uintmax_t i;
509 	int shmfd;
510 
511 	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
512 	if (shmfd < 0)
513 		err(-1, "test_shmfd: shm_open");
514 	close(shmfd);
515 	benchmark_start();
516 	for (i = 0; i < num; i++) {
517 		if (alarm_fired)
518 			break;
519 		shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
520 		if (shmfd < 0)
521 			err(-1, "test_shmfd: shm_open");
522 		close(shmfd);
523 	}
524 	benchmark_stop();
525 	return (i);
526 }
527 
528 uintmax_t
529 test_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
530 {
531 	struct stat sb;
532 	uintmax_t i;
533 	int shmfd;
534 
535 	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
536 	if (shmfd < 0)
537 		err(-1, "test_fstat_shmfd: shm_open");
538 	if (fstat(shmfd, &sb) < 0)
539 		err(-1, "test_fstat_shmfd: fstat");
540 	benchmark_start();
541 	for (i = 0; i < num; i++) {
542 		if (alarm_fired)
543 			break;
544 		(void)fstat(shmfd, &sb);
545 	}
546 	benchmark_stop();
547 	close(shmfd);
548 	return (i);
549 }
550 
551 uintmax_t
552 test_fork(uintmax_t num, uintmax_t int_arg, const char *path)
553 {
554 	pid_t pid;
555 	uintmax_t i;
556 
557 	pid = fork();
558 	if (pid < 0)
559 		err(-1, "test_fork: fork");
560 	if (pid == 0)
561 		_exit(0);
562 	if (waitpid(pid, NULL, 0) < 0)
563 		err(-1, "test_fork: waitpid");
564 	benchmark_start();
565 	for (i = 0; i < num; i++) {
566 		if (alarm_fired)
567 			break;
568 		pid = fork();
569 		if (pid < 0)
570 			err(-1, "test_fork: fork");
571 		if (pid == 0)
572 			_exit(0);
573 		if (waitpid(pid, NULL, 0) < 0)
574 			err(-1, "test_fork: waitpid");
575 	}
576 	benchmark_stop();
577 	return (i);
578 }
579 
580 uintmax_t
581 test_vfork(uintmax_t num, uintmax_t int_arg, const char *path)
582 {
583 	pid_t pid;
584 	uintmax_t i;
585 
586 	pid = vfork();
587 	if (pid < 0)
588 		err(-1, "test_vfork: vfork");
589 	if (pid == 0)
590 		_exit(0);
591 	if (waitpid(pid, NULL, 0) < 0)
592 		err(-1, "test_vfork: waitpid");
593 	benchmark_start();
594 	for (i = 0; i < num; i++) {
595 		if (alarm_fired)
596 			break;
597 		pid = vfork();
598 		if (pid < 0)
599 			err(-1, "test_vfork: vfork");
600 		if (pid == 0)
601 			_exit(0);
602 		if (waitpid(pid, NULL, 0) < 0)
603 			err(-1, "test_vfork: waitpid");
604 	}
605 	benchmark_stop();
606 	return (i);
607 }
608 
609 #define	USR_BIN_TRUE	"/usr/bin/true"
610 static char *execve_args[] = { USR_BIN_TRUE, NULL};
611 extern char **environ;
612 
613 uintmax_t
614 test_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
615 {
616 	pid_t pid;
617 	uintmax_t i;
618 
619 	pid = fork();
620 	if (pid < 0)
621 		err(-1, "test_fork_exec: fork");
622 	if (pid == 0) {
623 		(void)execve(USR_BIN_TRUE, execve_args, environ);
624 		err(-1, "execve");
625 	}
626 	if (waitpid(pid, NULL, 0) < 0)
627 		err(-1, "test_fork: waitpid");
628 	benchmark_start();
629 	for (i = 0; i < num; i++) {
630 		if (alarm_fired)
631 			break;
632 		pid = fork();
633 		if (pid < 0)
634 			err(-1, "test_fork_exec: fork");
635 		if (pid == 0) {
636 			(void)execve(USR_BIN_TRUE, execve_args, environ);
637 			err(-1, "test_fork_exec: execve");
638 		}
639 		if (waitpid(pid, NULL, 0) < 0)
640 			err(-1, "test_fork_exec: waitpid");
641 	}
642 	benchmark_stop();
643 	return (i);
644 }
645 
646 uintmax_t
647 test_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
648 {
649 	pid_t pid;
650 	uintmax_t i;
651 
652 	pid = vfork();
653 	if (pid < 0)
654 		err(-1, "test_vfork_exec: vfork");
655 	if (pid == 0) {
656 		(void)execve(USR_BIN_TRUE, execve_args, environ);
657 		err(-1, "test_vfork_exec: execve");
658 	}
659 	if (waitpid(pid, NULL, 0) < 0)
660 		err(-1, "test_vfork_exec: waitpid");
661 	benchmark_start();
662 	for (i = 0; i < num; i++) {
663 		if (alarm_fired)
664 			break;
665 		pid = vfork();
666 		if (pid < 0)
667 			err(-1, "test_vfork_exec: vfork");
668 		if (pid == 0) {
669 			(void)execve(USR_BIN_TRUE, execve_args, environ);
670 			err(-1, "execve");
671 		}
672 		if (waitpid(pid, NULL, 0) < 0)
673 			err(-1, "test_vfork_exec: waitpid");
674 	}
675 	benchmark_stop();
676 	return (i);
677 }
678 
679 uintmax_t
680 test_chroot(uintmax_t num, uintmax_t int_arg, const char *path)
681 {
682 	uintmax_t i;
683 
684 	if (chroot("/") < 0)
685 		err(-1, "test_chroot: chroot");
686 	benchmark_start();
687 	for (i = 0; i < num; i++) {
688 		if (alarm_fired)
689 			break;
690 		if (chroot("/") < 0)
691 			err(-1, "test_chroot: chroot");
692 	}
693 	benchmark_stop();
694 	return (i);
695 }
696 
697 uintmax_t
698 test_setuid(uintmax_t num, uintmax_t int_arg, const char *path)
699 {
700 	uid_t uid;
701 	uintmax_t i;
702 
703 	uid = getuid();
704 	if (setuid(uid) < 0)
705 		err(-1, "test_setuid: setuid");
706 	benchmark_start();
707 	for (i = 0; i < num; i++) {
708 		if (alarm_fired)
709 			break;
710 		if (setuid(uid) < 0)
711 			err(-1, "test_setuid: setuid");
712 	}
713 	benchmark_stop();
714 	return (i);
715 }
716 
717 struct test {
718 	const char	*t_name;
719 	uintmax_t	(*t_func)(uintmax_t, uintmax_t, const char *);
720 	int		 t_flags;
721 	uintmax_t	 t_int;
722 };
723 
724 #define	FLAG_PATH	0x00000001
725 
726 static const struct test tests[] = {
727 	{ "getuid", test_getuid },
728 	{ "getppid", test_getppid },
729 	{ "getresuid", test_getresuid },
730 	{ "clock_gettime", test_clock_gettime },
731 	{ "gettimeofday", test_gettimeofday },
732 	{ "getpriority", test_getpriority },
733 	{ "pipe", test_pipe },
734 	{ "select", test_select },
735 	{ "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
736 	{ "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
737 	{ "socketpair_stream", test_socketpair_stream },
738 	{ "socketpair_dgram", test_socketpair_dgram },
739 	{ "socket_tcp", test_socket_stream, .t_int = PF_INET },
740 	{ "socket_udp", test_socket_dgram, .t_int = PF_INET },
741 	{ "access", test_access, .t_flags = FLAG_PATH },
742 	{ "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
743 	{ "bad_open", test_bad_open },
744 	{ "open_close", test_open_close, .t_flags = FLAG_PATH },
745 	{ "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
746 	    .t_int = 1 },
747 	{ "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
748 	    .t_int = 10 },
749 	{ "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
750 	    .t_int = 100 },
751 	{ "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
752 	    .t_int = 1000 },
753 	{ "open_read_close_10000", test_open_read_close,
754 	    .t_flags = FLAG_PATH, .t_int = 10000 },
755 	{ "open_read_close_100000", test_open_read_close,
756 	    .t_flags = FLAG_PATH, .t_int = 100000 },
757 	{ "open_read_close_1000000", test_open_read_close,
758 	    .t_flags = FLAG_PATH, .t_int = 1000000 },
759 	{ "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
760 	{ "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
761 	{ "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
762 	{ "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
763 	{ "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
764 	{ "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
765 	{ "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
766 	{ "dup", test_dup },
767 	{ "shmfd", test_shmfd },
768 	{ "fstat_shmfd", test_fstat_shmfd },
769 	{ "fork", test_fork },
770 	{ "vfork", test_vfork },
771 	{ "fork_exec", test_fork_exec },
772 	{ "vfork_exec", test_vfork_exec },
773 	{ "chroot", test_chroot },
774 	{ "setuid", test_setuid },
775 };
776 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
777 
778 static void
779 usage(void)
780 {
781 	int i;
782 
783 	fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
784 	    "[-p path] [-s seconds] test\n");
785 	for (i = 0; i < tests_count; i++)
786 		fprintf(stderr, "  %s\n", tests[i].t_name);
787 	exit(-1);
788 }
789 
790 int
791 main(int argc, char *argv[])
792 {
793 	struct timespec ts_res;
794 	const struct test *the_test;
795 	const char *path;
796 	char *tmp_dir, *tmp_path;
797 	long long ll;
798 	char *endp;
799 	int ch, fd, error, i, j, k, rv;
800 	uintmax_t iterations, loops;
801 
802 	alarm_timeout = 1;
803 	iterations = 0;
804 	loops = 10;
805 	path = NULL;
806 	tmp_path = NULL;
807 	while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
808 		switch (ch) {
809 		case 'i':
810 			ll = strtol(optarg, &endp, 10);
811 			if (*endp != 0 || ll < 1)
812 				usage();
813 			iterations = ll;
814 			break;
815 
816 		case 'l':
817 			ll = strtol(optarg, &endp, 10);
818 			if (*endp != 0 || ll < 1 || ll > 100000)
819 				usage();
820 			loops = ll;
821 			break;
822 
823 		case 'p':
824 			path = optarg;
825 			break;
826 
827 		case 's':
828 			ll = strtol(optarg, &endp, 10);
829 			if (*endp != 0 || ll < 1 || ll > 60*60)
830 				usage();
831 			alarm_timeout = ll;
832 			break;
833 
834 		case '?':
835 		default:
836 			usage();
837 		}
838 	}
839 	argc -= optind;
840 	argv += optind;
841 
842 	if (iterations < 1 && alarm_timeout < 1)
843 		usage();
844 	if (iterations < 1)
845 		iterations = UINT64_MAX;
846 	if (loops < 1)
847 		loops = 1;
848 
849 	if (argc < 1)
850 		usage();
851 
852 	/*
853 	 * Validate test list and that, if a path is required, it is
854 	 * defined.
855 	 */
856 	for (j = 0; j < argc; j++) {
857 		the_test = NULL;
858 		for (i = 0; i < tests_count; i++) {
859 			if (strcmp(argv[j], tests[i].t_name) == 0)
860 				the_test = &tests[i];
861 		}
862 		if (the_test == NULL)
863 			usage();
864 		if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
865 			tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
866 			if (tmp_dir == NULL)
867 				err(1, "strdup");
868 			tmp_dir = mkdtemp(tmp_dir);
869 			if (tmp_dir == NULL)
870 				err(1, "mkdtemp");
871 			rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
872 			if (rv <= 0)
873 				err(1, "asprintf");
874 		}
875 	}
876 
877 	error = clock_getres(CLOCK_REALTIME, &ts_res);
878 	assert(error == 0);
879 	printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
880 	    (uintmax_t)ts_res.tv_nsec);
881 	printf("test\tloop\ttime\titerations\tperiteration\n");
882 
883 	for (j = 0; j < argc; j++) {
884 		uintmax_t calls, nsecsperit;
885 
886 		the_test = NULL;
887 		for (i = 0; i < tests_count; i++) {
888 			if (strcmp(argv[j], tests[i].t_name) == 0)
889 				the_test = &tests[i];
890 		}
891 
892 		if (tmp_path != NULL) {
893 			fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
894 			if (fd < 0)
895 				err(1, "cannot open %s", tmp_path);
896 			error = ftruncate(fd, 1000000);
897 			if (error != 0)
898 				err(1, "ftruncate");
899 			error = close(fd);
900 			if (error != 0)
901 				err(1, "close");
902 			path = tmp_path;
903 		}
904 
905 		/*
906 		 * Run one warmup, then do the real thing (loops) times.
907 		 */
908 		the_test->t_func(iterations, the_test->t_int, path);
909 		calls = 0;
910 		for (k = 0; k < loops; k++) {
911 			calls = the_test->t_func(iterations, the_test->t_int,
912 			    path);
913 			timespecsub(&ts_end, &ts_start);
914 			printf("%s\t%d\t", the_test->t_name, k);
915 			printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
916 			    (uintmax_t)ts_end.tv_nsec, calls);
917 
918 		/*
919 		 * Note.  This assumes that each iteration takes less than
920 		 * a second, and that our total nanoseconds doesn't exceed
921 		 * the room in our arithmetic unit.  Fine for system calls,
922 		 * but not for long things.
923 		 */
924 			nsecsperit = ts_end.tv_sec * 1000000000;
925 			nsecsperit += ts_end.tv_nsec;
926 			nsecsperit /= calls;
927 			printf("0.%09ju\n", (uintmax_t)nsecsperit);
928 		}
929 	}
930 
931 	if (tmp_path != NULL) {
932 		error = unlink(tmp_path);
933 		if (error != 0 && errno != ENOENT)
934 			warn("cannot unlink %s", tmp_path);
935 		error = rmdir(tmp_dir);
936 		if (error != 0)
937 			warn("cannot rmdir %s", tmp_dir);
938 	}
939 
940 	return (0);
941 }
942