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/socket.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <sys/wait.h>
38 
39 #include <assert.h>
40 #include <err.h>
41 #include <fcntl.h>
42 #include <inttypes.h>
43 #include <limits.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 
50 static struct timespec ts_start, ts_end;
51 static int alarm_timeout;
52 static volatile int alarm_fired;
53 
54 #define timespecsub(vvp, uvp)						\
55 	do {								\
56 		(vvp)->tv_sec -= (uvp)->tv_sec;				\
57 		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
58 		if ((vvp)->tv_nsec < 0) {				\
59 			(vvp)->tv_sec--;				\
60 			(vvp)->tv_nsec += 1000000000;			\
61 		}							\
62 	} while (0)
63 
64 static void
65 alarm_handler(int signum)
66 {
67 
68 	alarm_fired = 1;
69 }
70 
71 static void
72 benchmark_start(void)
73 {
74 	int error;
75 
76 	alarm_fired = 0;
77 	if (alarm_timeout) {
78 		signal(SIGALRM, alarm_handler);
79 		alarm(alarm_timeout);
80 	}
81 	error = clock_gettime(CLOCK_REALTIME, &ts_start);
82 	assert(error == 0);
83 }
84 
85 static void
86 benchmark_stop(void)
87 {
88 	int error;
89 
90 	error = clock_gettime(CLOCK_REALTIME, &ts_end);
91 	assert(error == 0);
92 }
93 
94 uintmax_t
95 test_getuid(uintmax_t num, uintmax_t int_arg, const char *path)
96 {
97 	uintmax_t i;
98 
99 	/*
100 	 * Thread-local data should require no locking if system
101 	 * call is MPSAFE.
102 	 */
103 	benchmark_start();
104 	for (i = 0; i < num; i++) {
105 		if (alarm_fired)
106 			break;
107 		getuid();
108 	}
109 	benchmark_stop();
110 	return (i);
111 }
112 
113 uintmax_t
114 test_getppid(uintmax_t num, uintmax_t int_arg, const char *path)
115 {
116 	uintmax_t i;
117 
118 	/*
119 	 * This is process-local, but can change, so will require a
120 	 * lock.
121 	 */
122 	benchmark_start();
123 	for (i = 0; i < num; i++) {
124 		if (alarm_fired)
125 			break;
126 		getppid();
127 	}
128 	benchmark_stop();
129 	return (i);
130 }
131 
132 uintmax_t
133 test_clock_gettime(uintmax_t num, uintmax_t int_arg, const char *path)
134 {
135 	struct timespec ts;
136 	uintmax_t i;
137 
138 	benchmark_start();
139 	for (i = 0; i < num; i++) {
140 		if (alarm_fired)
141 			break;
142 		(void)clock_gettime(CLOCK_REALTIME, &ts);
143 	}
144 	benchmark_stop();
145 	return (i);
146 }
147 
148 uintmax_t
149 test_gettimeofday(uintmax_t num, uintmax_t int_arg, const char *path)
150 {
151 	struct timeval tv;
152 	uintmax_t i;
153 
154 	benchmark_start();
155 	for (i = 0; i < num; i++) {
156 		if (alarm_fired)
157 			break;
158 		(void)gettimeofday(&tv, NULL);
159 	}
160 	benchmark_stop();
161 	return (i);
162 }
163 
164 uintmax_t
165 test_pipe(uintmax_t num, uintmax_t int_arg, const char *path)
166 {
167 	int fd[2], i;
168 
169 	/*
170 	 * pipe creation is expensive, as it will allocate a new file
171 	 * descriptor, allocate a new pipe, hook it all up, and return.
172 	 * Destroying is also expensive, as we now have to free up
173 	 * the file descriptors and return the pipe.
174 	 */
175 	if (pipe(fd) < 0)
176 		err(-1, "test_pipe: pipe");
177 	close(fd[0]);
178 	close(fd[1]);
179 	benchmark_start();
180 	for (i = 0; i < num; i++) {
181 		if (alarm_fired)
182 			break;
183 		if (pipe(fd) == -1)
184 			err(-1, "test_pipe: pipe");
185 		close(fd[0]);
186 		close(fd[1]);
187 	}
188 	benchmark_stop();
189 	return (i);
190 }
191 
192 uintmax_t
193 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path)
194 {
195 	uintmax_t i;
196 	int so;
197 
198 	so = socket(int_arg, SOCK_STREAM, 0);
199 	if (so < 0)
200 		err(-1, "test_socket_stream: socket");
201 	close(so);
202 	benchmark_start();
203 	for (i = 0; i < num; i++) {
204 		if (alarm_fired)
205 			break;
206 		so = socket(int_arg, SOCK_STREAM, 0);
207 		if (so == -1)
208 			err(-1, "test_socket_stream: socket");
209 		close(so);
210 	}
211 	benchmark_stop();
212 	return (i);
213 }
214 
215 uintmax_t
216 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
217 {
218 	uintmax_t i;
219 	int so;
220 
221 	so = socket(int_arg, SOCK_DGRAM, 0);
222 	if (so < 0)
223 		err(-1, "test_socket_dgram: socket");
224 	close(so);
225 	benchmark_start();
226 	for (i = 0; i < num; i++) {
227 		if (alarm_fired)
228 			break;
229 		so = socket(int_arg, SOCK_DGRAM, 0);
230 		if (so == -1)
231 			err(-1, "test_socket_dgram: socket");
232 		close(so);
233 	}
234 	benchmark_stop();
235 	return (i);
236 }
237 
238 uintmax_t
239 test_socketpair_stream(uintmax_t num, uintmax_t int_arg, const char *path)
240 {
241 	uintmax_t i;
242 	int so[2];
243 
244 	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
245 		err(-1, "test_socketpair_stream: socketpair");
246 	close(so[0]);
247 	close(so[1]);
248 	benchmark_start();
249 	for (i = 0; i < num; i++) {
250 		if (alarm_fired)
251 			break;
252 		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
253 			err(-1, "test_socketpair_stream: socketpair");
254 		close(so[0]);
255 		close(so[1]);
256 	}
257 	benchmark_stop();
258 	return (i);
259 }
260 
261 uintmax_t
262 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
263 {
264 	uintmax_t i;
265 	int so[2];
266 
267 	if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
268 		err(-1, "test_socketpair_dgram: socketpair");
269 	close(so[0]);
270 	close(so[1]);
271 	benchmark_start();
272 	for (i = 0; i < num; i++) {
273 		if (alarm_fired)
274 			break;
275 		if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
276 			err(-1, "test_socketpair_dgram: socketpair");
277 		close(so[0]);
278 		close(so[1]);
279 	}
280 	benchmark_stop();
281 	return (i);
282 }
283 
284 uintmax_t
285 test_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path)
286 {
287 	uintmax_t i;
288 	int fd;
289 
290 	(void)unlink(path);
291 	fd = open(path, O_RDWR | O_CREAT, 0600);
292 	if (fd < 0)
293 		err(-1, "test_create_unlink: create: %s", path);
294 	close(fd);
295 	if (unlink(path) < 0)
296 		err(-1, "test_create_unlink: unlink: %s", path);
297 	benchmark_start();
298 	for (i = 0; i < num; i++) {
299 		if (alarm_fired)
300 			break;
301 		fd = open(path, O_RDWR | O_CREAT, 0600);
302 		if (fd < 0)
303 			err(-1, "test_create_unlink: create: %s", path);
304 		close(fd);
305 		if (unlink(path) < 0)
306 			err(-1, "test_create_unlink: unlink: %s", path);
307 	}
308 	benchmark_stop();
309 	return (i);
310 }
311 
312 uintmax_t
313 test_open_close(uintmax_t num, uintmax_t int_arg, const char *path)
314 {
315 	uintmax_t i;
316 	int fd;
317 
318 	fd = open(path, O_RDONLY);
319 	if (fd < 0)
320 		err(-1, "test_open_close: %s", path);
321 	close(fd);
322 
323 	benchmark_start();
324 	for (i = 0; i < num; i++) {
325 		if (alarm_fired)
326 			break;
327 		fd = open(path, O_RDONLY);
328 		if (fd < 0)
329 			err(-1, "test_open_close: %s", path);
330 		close(fd);
331 	}
332 	benchmark_stop();
333 	return (i);
334 }
335 
336 uintmax_t
337 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
338 {
339 	char buf[int_arg];
340 	uintmax_t i;
341 	int fd;
342 
343 	fd = open(path, O_RDONLY);
344 	if (fd < 0)
345 		err(-1, "test_open_read: %s", path);
346 	(void)pread(fd, buf, int_arg, 0);
347 
348 	benchmark_start();
349 	for (i = 0; i < num; i++) {
350 		if (alarm_fired)
351 			break;
352 		(void)pread(fd, buf, int_arg, 0);
353 	}
354 	benchmark_stop();
355 	close(fd);
356 	return (i);
357 }
358 
359 uintmax_t
360 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
361 {
362 	char buf[int_arg];
363 	uintmax_t i;
364 	int fd;
365 
366 	fd = open(path, O_RDONLY);
367 	if (fd < 0)
368 		err(-1, "test_open_read_close: %s", path);
369 	(void)read(fd, buf, int_arg);
370 	close(fd);
371 
372 	benchmark_start();
373 	for (i = 0; i < num; i++) {
374 		if (alarm_fired)
375 			break;
376 		fd = open(path, O_RDONLY);
377 		if (fd < 0)
378 			err(-1, "test_open_read_close: %s", path);
379 		(void)read(fd, buf, int_arg);
380 		close(fd);
381 	}
382 	benchmark_stop();
383 	return (i);
384 }
385 
386 uintmax_t
387 test_dup(uintmax_t num, uintmax_t int_arg, const char *path)
388 {
389 	int fd, i, shmfd;
390 
391 	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
392 	if (shmfd < 0)
393 		err(-1, "test_dup: shm_open");
394 	fd = dup(shmfd);
395 	if (fd >= 0)
396 		close(fd);
397 	benchmark_start();
398 	for (i = 0; i < num; i++) {
399 		if (alarm_fired)
400 			break;
401 		fd = dup(shmfd);
402 		if (fd >= 0)
403 			close(fd);
404 	}
405 	benchmark_stop();
406 	close(shmfd);
407 	return (i);
408 }
409 
410 uintmax_t
411 test_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
412 {
413 	uintmax_t i;
414 	int shmfd;
415 
416 	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
417 	if (shmfd < 0)
418 		err(-1, "test_shmfd: shm_open");
419 	close(shmfd);
420 	benchmark_start();
421 	for (i = 0; i < num; i++) {
422 		if (alarm_fired)
423 			break;
424 		shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
425 		if (shmfd < 0)
426 			err(-1, "test_shmfd: shm_open");
427 		close(shmfd);
428 	}
429 	benchmark_stop();
430 	return (i);
431 }
432 
433 uintmax_t
434 test_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
435 {
436 	struct stat sb;
437 	uintmax_t i;
438 	int shmfd;
439 
440 	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
441 	if (shmfd < 0)
442 		err(-1, "test_fstat_shmfd: shm_open");
443 	if (fstat(shmfd, &sb) < 0)
444 		err(-1, "test_fstat_shmfd: fstat");
445 	benchmark_start();
446 	for (i = 0; i < num; i++) {
447 		if (alarm_fired)
448 			break;
449 		(void)fstat(shmfd, &sb);
450 	}
451 	benchmark_stop();
452 	close(shmfd);
453 	return (i);
454 }
455 
456 uintmax_t
457 test_fork(uintmax_t num, uintmax_t int_arg, const char *path)
458 {
459 	pid_t pid;
460 	uintmax_t i;
461 
462 	pid = fork();
463 	if (pid < 0)
464 		err(-1, "test_fork: fork");
465 	if (pid == 0)
466 		_exit(0);
467 	if (waitpid(pid, NULL, 0) < 0)
468 		err(-1, "test_fork: waitpid");
469 	benchmark_start();
470 	for (i = 0; i < num; i++) {
471 		if (alarm_fired)
472 			break;
473 		pid = fork();
474 		if (pid < 0)
475 			err(-1, "test_fork: fork");
476 		if (pid == 0)
477 			_exit(0);
478 		if (waitpid(pid, NULL, 0) < 0)
479 			err(-1, "test_fork: waitpid");
480 	}
481 	benchmark_stop();
482 	return (i);
483 }
484 
485 uintmax_t
486 test_vfork(uintmax_t num, uintmax_t int_arg, const char *path)
487 {
488 	pid_t pid;
489 	uintmax_t i;
490 
491 	pid = vfork();
492 	if (pid < 0)
493 		err(-1, "test_vfork: vfork");
494 	if (pid == 0)
495 		_exit(0);
496 	if (waitpid(pid, NULL, 0) < 0)
497 		err(-1, "test_vfork: waitpid");
498 	benchmark_start();
499 	for (i = 0; i < num; i++) {
500 		if (alarm_fired)
501 			break;
502 		pid = vfork();
503 		if (pid < 0)
504 			err(-1, "test_vfork: vfork");
505 		if (pid == 0)
506 			_exit(0);
507 		if (waitpid(pid, NULL, 0) < 0)
508 			err(-1, "test_vfork: waitpid");
509 	}
510 	benchmark_stop();
511 	return (i);
512 }
513 
514 #define	USR_BIN_TRUE	"/usr/bin/true"
515 static char *execve_args[] = { USR_BIN_TRUE, NULL};
516 extern char **environ;
517 
518 uintmax_t
519 test_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
520 {
521 	pid_t pid;
522 	uintmax_t i;
523 
524 	pid = fork();
525 	if (pid < 0)
526 		err(-1, "test_fork_exec: fork");
527 	if (pid == 0) {
528 		(void)execve(USR_BIN_TRUE, execve_args, environ);
529 		err(-1, "execve");
530 	}
531 	if (waitpid(pid, NULL, 0) < 0)
532 		err(-1, "test_fork: waitpid");
533 	benchmark_start();
534 	for (i = 0; i < num; i++) {
535 		if (alarm_fired)
536 			break;
537 		pid = fork();
538 		if (pid < 0)
539 			err(-1, "test_fork_exec: fork");
540 		if (pid == 0) {
541 			(void)execve(USR_BIN_TRUE, execve_args, environ);
542 			err(-1, "test_fork_exec: execve");
543 		}
544 		if (waitpid(pid, NULL, 0) < 0)
545 			err(-1, "test_fork_exec: waitpid");
546 	}
547 	benchmark_stop();
548 	return (i);
549 }
550 
551 uintmax_t
552 test_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
553 {
554 	pid_t pid;
555 	uintmax_t i;
556 
557 	pid = vfork();
558 	if (pid < 0)
559 		err(-1, "test_vfork_exec: vfork");
560 	if (pid == 0) {
561 		(void)execve(USR_BIN_TRUE, execve_args, environ);
562 		err(-1, "test_vfork_exec: execve");
563 	}
564 	if (waitpid(pid, NULL, 0) < 0)
565 		err(-1, "test_vfork_exec: waitpid");
566 	benchmark_start();
567 	for (i = 0; i < num; i++) {
568 		if (alarm_fired)
569 			break;
570 		pid = vfork();
571 		if (pid < 0)
572 			err(-1, "test_vfork_exec: vfork");
573 		if (pid == 0) {
574 			(void)execve(USR_BIN_TRUE, execve_args, environ);
575 			err(-1, "execve");
576 		}
577 		if (waitpid(pid, NULL, 0) < 0)
578 			err(-1, "test_vfork_exec: waitpid");
579 	}
580 	benchmark_stop();
581 	return (i);
582 }
583 
584 uintmax_t
585 test_chroot(uintmax_t num, uintmax_t int_arg, const char *path)
586 {
587 	uintmax_t i;
588 
589 	if (chroot("/") < 0)
590 		err(-1, "test_chroot: chroot");
591 	benchmark_start();
592 	for (i = 0; i < num; i++) {
593 		if (alarm_fired)
594 			break;
595 		if (chroot("/") < 0)
596 			err(-1, "test_chroot: chroot");
597 	}
598 	benchmark_stop();
599 	return (i);
600 }
601 
602 uintmax_t
603 test_setuid(uintmax_t num, uintmax_t int_arg, const char *path)
604 {
605 	uid_t uid;
606 	uintmax_t i;
607 
608 	uid = getuid();
609 	if (setuid(uid) < 0)
610 		err(-1, "test_setuid: setuid");
611 	benchmark_start();
612 	for (i = 0; i < num; i++) {
613 		if (alarm_fired)
614 			break;
615 		if (setuid(uid) < 0)
616 			err(-1, "test_setuid: setuid");
617 	}
618 	benchmark_stop();
619 	return (i);
620 }
621 
622 struct test {
623 	const char	*t_name;
624 	uintmax_t	(*t_func)(uintmax_t, uintmax_t, const char *);
625 	int		 t_flags;
626 	uintmax_t	 t_int;
627 };
628 
629 #define	FLAG_PATH	0x00000001
630 
631 static const struct test tests[] = {
632 	{ "getuid", test_getuid },
633 	{ "getppid", test_getppid },
634 	{ "clock_gettime", test_clock_gettime },
635 	{ "gettimeofday", test_gettimeofday },
636 	{ "pipe", test_pipe },
637 	{ "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
638 	{ "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
639 	{ "socketpair_stream", test_socketpair_stream },
640 	{ "socketpair_dgram", test_socketpair_dgram },
641 	{ "socket_tcp", test_socket_stream, .t_int = PF_INET },
642 	{ "socket_udp", test_socket_dgram, .t_int = PF_INET },
643 	{ "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
644 	{ "open_close", test_open_close, .t_flags = FLAG_PATH },
645 	{ "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
646 	    .t_int = 1 },
647 	{ "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
648 	    .t_int = 10 },
649 	{ "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
650 	    .t_int = 100 },
651 	{ "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
652 	    .t_int = 1000 },
653 	{ "open_read_close_10000", test_open_read_close,
654 	    .t_flags = FLAG_PATH, .t_int = 10000 },
655 	{ "open_read_close_100000", test_open_read_close,
656 	    .t_flags = FLAG_PATH, .t_int = 100000 },
657 	{ "open_read_close_1000000", test_open_read_close,
658 	    .t_flags = FLAG_PATH, .t_int = 1000000 },
659 	{ "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
660 	{ "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
661 	{ "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
662 	{ "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
663 	{ "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
664 	{ "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
665 	{ "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
666 	{ "dup", test_dup },
667 	{ "shmfd", test_shmfd },
668 	{ "fstat_shmfd", test_fstat_shmfd },
669 	{ "fork", test_fork },
670 	{ "vfork", test_vfork },
671 	{ "fork_exec", test_fork_exec },
672 	{ "vfork_exec", test_vfork_exec },
673 	{ "chroot", test_chroot },
674 	{ "setuid", test_setuid },
675 };
676 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
677 
678 static void
679 usage(void)
680 {
681 	int i;
682 
683 	fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
684 	    "[-p path] [-s seconds] test\n");
685 	for (i = 0; i < tests_count; i++)
686 		fprintf(stderr, "  %s\n", tests[i].t_name);
687 	exit(-1);
688 }
689 
690 int
691 main(int argc, char *argv[])
692 {
693 	struct timespec ts_res;
694 	const struct test *the_test;
695 	const char *path;
696 	long long ll;
697 	char *endp;
698 	int ch, error, i, j, k;
699 	uintmax_t iterations, loops;
700 
701 	alarm_timeout = 1;
702 	iterations = 0;
703 	loops = 10;
704 	path = NULL;
705 	while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
706 		switch (ch) {
707 		case 'i':
708 			ll = strtol(optarg, &endp, 10);
709 			if (*endp != 0 || ll < 1 || ll > 100000)
710 				usage();
711 			iterations = ll;
712 			break;
713 
714 		case 'l':
715 			ll = strtol(optarg, &endp, 10);
716 			if (*endp != 0 || ll < 1 || ll > 100000)
717 				usage();
718 			loops = ll;
719 			break;
720 
721 		case 'p':
722 			path = optarg;
723 			break;
724 
725 		case 's':
726 			ll = strtol(optarg, &endp, 10);
727 			if (*endp != 0 || ll < 1 || ll > 60*60)
728 				usage();
729 			alarm_timeout = ll;
730 			break;
731 
732 		case '?':
733 		default:
734 			usage();
735 		}
736 	}
737 	argc -= optind;
738 	argv += optind;
739 
740 	if (iterations < 1 && alarm_timeout < 1)
741 		usage();
742 	if (iterations < 1)
743 		iterations = UINT64_MAX;
744 	if (loops < 1)
745 		loops = 1;
746 
747 	if (argc < 1)
748 		usage();
749 
750 	/*
751 	 * Validate test list and that, if a path is required, it is
752 	 * defined.
753 	 */
754 	for (j = 0; j < argc; j++) {
755 		the_test = NULL;
756 		for (i = 0; i < tests_count; i++) {
757 			if (strcmp(argv[j], tests[i].t_name) == 0)
758 				the_test = &tests[i];
759 		}
760 		if (the_test == NULL)
761 			usage();
762 		if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
763 			errx(-1, "%s requires -p", the_test->t_name);
764 		}
765 	}
766 
767 	error = clock_getres(CLOCK_REALTIME, &ts_res);
768 	assert(error == 0);
769 	printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
770 	    (uintmax_t)ts_res.tv_nsec);
771 	printf("test\tloop\ttime\titerations\tperiteration\n");
772 
773 	for (j = 0; j < argc; j++) {
774 		uintmax_t calls, nsecsperit;
775 
776 		the_test = NULL;
777 		for (i = 0; i < tests_count; i++) {
778 			if (strcmp(argv[j], tests[i].t_name) == 0)
779 				the_test = &tests[i];
780 		}
781 
782 		/*
783 		 * Run one warmup, then do the real thing (loops) times.
784 		 */
785 		the_test->t_func(iterations, the_test->t_int, path);
786 		calls = 0;
787 		for (k = 0; k < loops; k++) {
788 			calls = the_test->t_func(iterations, the_test->t_int,
789 			    path);
790 			timespecsub(&ts_end, &ts_start);
791 			printf("%s\t%d\t", the_test->t_name, k);
792 			printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
793 			    (uintmax_t)ts_end.tv_nsec, calls);
794 
795 		/*
796 		 * Note.  This assumes that each iteration takes less than
797 		 * a second, and that our total nanoseconds doesn't exceed
798 		 * the room in our arithmetic unit.  Fine for system calls,
799 		 * but not for long things.
800 		 */
801 			nsecsperit = ts_end.tv_sec * 1000000000;
802 			nsecsperit += ts_end.tv_nsec;
803 			nsecsperit /= calls;
804 			printf("0.%09ju\n", (uintmax_t)nsecsperit);
805 		}
806 	}
807 	return (0);
808 }
809