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