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