syscall_timing.c revision 332627
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 332627 2018-04-16 17:29:50Z 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_access(uintmax_t num, uintmax_t int_arg, const char *path)
343{
344	uintmax_t i;
345	int fd;
346
347	fd = access(path, O_RDONLY);
348	if (fd < 0)
349		err(-1, "test_access: %s", path);
350	close(fd);
351
352	benchmark_start();
353	for (i = 0; i < num; i++) {
354		if (alarm_fired)
355			break;
356		access(path, O_RDONLY);
357		close(fd);
358	}
359	benchmark_stop();
360	return (i);
361}
362
363uintmax_t
364test_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path)
365{
366	uintmax_t i;
367	int fd;
368
369	(void)unlink(path);
370	fd = open(path, O_RDWR | O_CREAT, 0600);
371	if (fd < 0)
372		err(-1, "test_create_unlink: create: %s", path);
373	close(fd);
374	if (unlink(path) < 0)
375		err(-1, "test_create_unlink: unlink: %s", path);
376	benchmark_start();
377	for (i = 0; i < num; i++) {
378		if (alarm_fired)
379			break;
380		fd = open(path, O_RDWR | O_CREAT, 0600);
381		if (fd < 0)
382			err(-1, "test_create_unlink: create: %s", path);
383		close(fd);
384		if (unlink(path) < 0)
385			err(-1, "test_create_unlink: unlink: %s", path);
386	}
387	benchmark_stop();
388	return (i);
389}
390
391uintmax_t
392test_open_close(uintmax_t num, uintmax_t int_arg, const char *path)
393{
394	uintmax_t i;
395	int fd;
396
397	fd = open(path, O_RDONLY);
398	if (fd < 0)
399		err(-1, "test_open_close: %s", path);
400	close(fd);
401
402	benchmark_start();
403	for (i = 0; i < num; i++) {
404		if (alarm_fired)
405			break;
406		fd = open(path, O_RDONLY);
407		if (fd < 0)
408			err(-1, "test_open_close: %s", path);
409		close(fd);
410	}
411	benchmark_stop();
412	return (i);
413}
414
415uintmax_t
416test_bad_open(uintmax_t num, uintmax_t int_arg, const char *path)
417{
418	uintmax_t i;
419
420	benchmark_start();
421	for (i = 0; i < num; i++) {
422		if (alarm_fired)
423			break;
424		open("", O_RDONLY);
425	}
426	benchmark_stop();
427	return (i);
428}
429
430uintmax_t
431test_read(uintmax_t num, uintmax_t int_arg, const char *path)
432{
433	char buf[int_arg];
434	uintmax_t i;
435	int fd;
436
437	fd = open(path, O_RDONLY);
438	if (fd < 0)
439		err(-1, "test_open_read: %s", path);
440	(void)pread(fd, buf, int_arg, 0);
441
442	benchmark_start();
443	for (i = 0; i < num; i++) {
444		if (alarm_fired)
445			break;
446		(void)pread(fd, buf, int_arg, 0);
447	}
448	benchmark_stop();
449	close(fd);
450	return (i);
451}
452
453uintmax_t
454test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
455{
456	char buf[int_arg];
457	uintmax_t i;
458	int fd;
459
460	fd = open(path, O_RDONLY);
461	if (fd < 0)
462		err(-1, "test_open_read_close: %s", path);
463	(void)read(fd, buf, int_arg);
464	close(fd);
465
466	benchmark_start();
467	for (i = 0; i < num; i++) {
468		if (alarm_fired)
469			break;
470		fd = open(path, O_RDONLY);
471		if (fd < 0)
472			err(-1, "test_open_read_close: %s", path);
473		(void)read(fd, buf, int_arg);
474		close(fd);
475	}
476	benchmark_stop();
477	return (i);
478}
479
480uintmax_t
481test_dup(uintmax_t num, uintmax_t int_arg, const char *path)
482{
483	int fd, i, shmfd;
484
485	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
486	if (shmfd < 0)
487		err(-1, "test_dup: shm_open");
488	fd = dup(shmfd);
489	if (fd >= 0)
490		close(fd);
491	benchmark_start();
492	for (i = 0; i < num; i++) {
493		if (alarm_fired)
494			break;
495		fd = dup(shmfd);
496		if (fd >= 0)
497			close(fd);
498	}
499	benchmark_stop();
500	close(shmfd);
501	return (i);
502}
503
504uintmax_t
505test_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
506{
507	uintmax_t i;
508	int shmfd;
509
510	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
511	if (shmfd < 0)
512		err(-1, "test_shmfd: shm_open");
513	close(shmfd);
514	benchmark_start();
515	for (i = 0; i < num; i++) {
516		if (alarm_fired)
517			break;
518		shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
519		if (shmfd < 0)
520			err(-1, "test_shmfd: shm_open");
521		close(shmfd);
522	}
523	benchmark_stop();
524	return (i);
525}
526
527uintmax_t
528test_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
529{
530	struct stat sb;
531	uintmax_t i;
532	int shmfd;
533
534	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
535	if (shmfd < 0)
536		err(-1, "test_fstat_shmfd: shm_open");
537	if (fstat(shmfd, &sb) < 0)
538		err(-1, "test_fstat_shmfd: fstat");
539	benchmark_start();
540	for (i = 0; i < num; i++) {
541		if (alarm_fired)
542			break;
543		(void)fstat(shmfd, &sb);
544	}
545	benchmark_stop();
546	close(shmfd);
547	return (i);
548}
549
550uintmax_t
551test_fork(uintmax_t num, uintmax_t int_arg, const char *path)
552{
553	pid_t pid;
554	uintmax_t i;
555
556	pid = fork();
557	if (pid < 0)
558		err(-1, "test_fork: fork");
559	if (pid == 0)
560		_exit(0);
561	if (waitpid(pid, NULL, 0) < 0)
562		err(-1, "test_fork: waitpid");
563	benchmark_start();
564	for (i = 0; i < num; i++) {
565		if (alarm_fired)
566			break;
567		pid = fork();
568		if (pid < 0)
569			err(-1, "test_fork: fork");
570		if (pid == 0)
571			_exit(0);
572		if (waitpid(pid, NULL, 0) < 0)
573			err(-1, "test_fork: waitpid");
574	}
575	benchmark_stop();
576	return (i);
577}
578
579uintmax_t
580test_vfork(uintmax_t num, uintmax_t int_arg, const char *path)
581{
582	pid_t pid;
583	uintmax_t i;
584
585	pid = vfork();
586	if (pid < 0)
587		err(-1, "test_vfork: vfork");
588	if (pid == 0)
589		_exit(0);
590	if (waitpid(pid, NULL, 0) < 0)
591		err(-1, "test_vfork: waitpid");
592	benchmark_start();
593	for (i = 0; i < num; i++) {
594		if (alarm_fired)
595			break;
596		pid = vfork();
597		if (pid < 0)
598			err(-1, "test_vfork: vfork");
599		if (pid == 0)
600			_exit(0);
601		if (waitpid(pid, NULL, 0) < 0)
602			err(-1, "test_vfork: waitpid");
603	}
604	benchmark_stop();
605	return (i);
606}
607
608#define	USR_BIN_TRUE	"/usr/bin/true"
609static char *execve_args[] = { USR_BIN_TRUE, NULL};
610extern char **environ;
611
612uintmax_t
613test_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
614{
615	pid_t pid;
616	uintmax_t i;
617
618	pid = fork();
619	if (pid < 0)
620		err(-1, "test_fork_exec: fork");
621	if (pid == 0) {
622		(void)execve(USR_BIN_TRUE, execve_args, environ);
623		err(-1, "execve");
624	}
625	if (waitpid(pid, NULL, 0) < 0)
626		err(-1, "test_fork: waitpid");
627	benchmark_start();
628	for (i = 0; i < num; i++) {
629		if (alarm_fired)
630			break;
631		pid = fork();
632		if (pid < 0)
633			err(-1, "test_fork_exec: fork");
634		if (pid == 0) {
635			(void)execve(USR_BIN_TRUE, execve_args, environ);
636			err(-1, "test_fork_exec: execve");
637		}
638		if (waitpid(pid, NULL, 0) < 0)
639			err(-1, "test_fork_exec: waitpid");
640	}
641	benchmark_stop();
642	return (i);
643}
644
645uintmax_t
646test_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
647{
648	pid_t pid;
649	uintmax_t i;
650
651	pid = vfork();
652	if (pid < 0)
653		err(-1, "test_vfork_exec: vfork");
654	if (pid == 0) {
655		(void)execve(USR_BIN_TRUE, execve_args, environ);
656		err(-1, "test_vfork_exec: execve");
657	}
658	if (waitpid(pid, NULL, 0) < 0)
659		err(-1, "test_vfork_exec: waitpid");
660	benchmark_start();
661	for (i = 0; i < num; i++) {
662		if (alarm_fired)
663			break;
664		pid = vfork();
665		if (pid < 0)
666			err(-1, "test_vfork_exec: vfork");
667		if (pid == 0) {
668			(void)execve(USR_BIN_TRUE, execve_args, environ);
669			err(-1, "execve");
670		}
671		if (waitpid(pid, NULL, 0) < 0)
672			err(-1, "test_vfork_exec: waitpid");
673	}
674	benchmark_stop();
675	return (i);
676}
677
678uintmax_t
679test_chroot(uintmax_t num, uintmax_t int_arg, const char *path)
680{
681	uintmax_t i;
682
683	if (chroot("/") < 0)
684		err(-1, "test_chroot: chroot");
685	benchmark_start();
686	for (i = 0; i < num; i++) {
687		if (alarm_fired)
688			break;
689		if (chroot("/") < 0)
690			err(-1, "test_chroot: chroot");
691	}
692	benchmark_stop();
693	return (i);
694}
695
696uintmax_t
697test_setuid(uintmax_t num, uintmax_t int_arg, const char *path)
698{
699	uid_t uid;
700	uintmax_t i;
701
702	uid = getuid();
703	if (setuid(uid) < 0)
704		err(-1, "test_setuid: setuid");
705	benchmark_start();
706	for (i = 0; i < num; i++) {
707		if (alarm_fired)
708			break;
709		if (setuid(uid) < 0)
710			err(-1, "test_setuid: setuid");
711	}
712	benchmark_stop();
713	return (i);
714}
715
716struct test {
717	const char	*t_name;
718	uintmax_t	(*t_func)(uintmax_t, uintmax_t, const char *);
719	int		 t_flags;
720	uintmax_t	 t_int;
721};
722
723#define	FLAG_PATH	0x00000001
724
725static const struct test tests[] = {
726	{ "getuid", test_getuid },
727	{ "getppid", test_getppid },
728	{ "getresuid", test_getresuid },
729	{ "clock_gettime", test_clock_gettime },
730	{ "gettimeofday", test_gettimeofday },
731	{ "getpriority", test_getpriority },
732	{ "pipe", test_pipe },
733	{ "select", test_select },
734	{ "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
735	{ "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
736	{ "socketpair_stream", test_socketpair_stream },
737	{ "socketpair_dgram", test_socketpair_dgram },
738	{ "socket_tcp", test_socket_stream, .t_int = PF_INET },
739	{ "socket_udp", test_socket_dgram, .t_int = PF_INET },
740	{ "access", test_access, .t_flags = FLAG_PATH },
741	{ "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
742	{ "bad_open", test_bad_open },
743	{ "open_close", test_open_close, .t_flags = FLAG_PATH },
744	{ "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
745	    .t_int = 1 },
746	{ "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
747	    .t_int = 10 },
748	{ "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
749	    .t_int = 100 },
750	{ "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
751	    .t_int = 1000 },
752	{ "open_read_close_10000", test_open_read_close,
753	    .t_flags = FLAG_PATH, .t_int = 10000 },
754	{ "open_read_close_100000", test_open_read_close,
755	    .t_flags = FLAG_PATH, .t_int = 100000 },
756	{ "open_read_close_1000000", test_open_read_close,
757	    .t_flags = FLAG_PATH, .t_int = 1000000 },
758	{ "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
759	{ "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
760	{ "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
761	{ "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
762	{ "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
763	{ "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
764	{ "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
765	{ "dup", test_dup },
766	{ "shmfd", test_shmfd },
767	{ "fstat_shmfd", test_fstat_shmfd },
768	{ "fork", test_fork },
769	{ "vfork", test_vfork },
770	{ "fork_exec", test_fork_exec },
771	{ "vfork_exec", test_vfork_exec },
772	{ "chroot", test_chroot },
773	{ "setuid", test_setuid },
774};
775static const int tests_count = sizeof(tests) / sizeof(tests[0]);
776
777static void
778usage(void)
779{
780	int i;
781
782	fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
783	    "[-p path] [-s seconds] test\n");
784	for (i = 0; i < tests_count; i++)
785		fprintf(stderr, "  %s\n", tests[i].t_name);
786	exit(-1);
787}
788
789int
790main(int argc, char *argv[])
791{
792	struct timespec ts_res;
793	const struct test *the_test;
794	const char *path;
795	long long ll;
796	char *endp;
797	int ch, error, i, j, k;
798	uintmax_t iterations, loops;
799
800	alarm_timeout = 1;
801	iterations = 0;
802	loops = 10;
803	path = NULL;
804	while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
805		switch (ch) {
806		case 'i':
807			ll = strtol(optarg, &endp, 10);
808			if (*endp != 0 || ll < 1)
809				usage();
810			iterations = ll;
811			break;
812
813		case 'l':
814			ll = strtol(optarg, &endp, 10);
815			if (*endp != 0 || ll < 1 || ll > 100000)
816				usage();
817			loops = ll;
818			break;
819
820		case 'p':
821			path = optarg;
822			break;
823
824		case 's':
825			ll = strtol(optarg, &endp, 10);
826			if (*endp != 0 || ll < 1 || ll > 60*60)
827				usage();
828			alarm_timeout = ll;
829			break;
830
831		case '?':
832		default:
833			usage();
834		}
835	}
836	argc -= optind;
837	argv += optind;
838
839	if (iterations < 1 && alarm_timeout < 1)
840		usage();
841	if (iterations < 1)
842		iterations = UINT64_MAX;
843	if (loops < 1)
844		loops = 1;
845
846	if (argc < 1)
847		usage();
848
849	/*
850	 * Validate test list and that, if a path is required, it is
851	 * defined.
852	 */
853	for (j = 0; j < argc; j++) {
854		the_test = NULL;
855		for (i = 0; i < tests_count; i++) {
856			if (strcmp(argv[j], tests[i].t_name) == 0)
857				the_test = &tests[i];
858		}
859		if (the_test == NULL)
860			usage();
861		if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
862			errx(-1, "%s requires -p", the_test->t_name);
863		}
864	}
865
866	error = clock_getres(CLOCK_REALTIME, &ts_res);
867	assert(error == 0);
868	printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
869	    (uintmax_t)ts_res.tv_nsec);
870	printf("test\tloop\ttime\titerations\tperiteration\n");
871
872	for (j = 0; j < argc; j++) {
873		uintmax_t calls, nsecsperit;
874
875		the_test = NULL;
876		for (i = 0; i < tests_count; i++) {
877			if (strcmp(argv[j], tests[i].t_name) == 0)
878				the_test = &tests[i];
879		}
880
881		/*
882		 * Run one warmup, then do the real thing (loops) times.
883		 */
884		the_test->t_func(iterations, the_test->t_int, path);
885		calls = 0;
886		for (k = 0; k < loops; k++) {
887			calls = the_test->t_func(iterations, the_test->t_int,
888			    path);
889			timespecsub(&ts_end, &ts_start);
890			printf("%s\t%d\t", the_test->t_name, k);
891			printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
892			    (uintmax_t)ts_end.tv_nsec, calls);
893
894		/*
895		 * Note.  This assumes that each iteration takes less than
896		 * a second, and that our total nanoseconds doesn't exceed
897		 * the room in our arithmetic unit.  Fine for system calls,
898		 * but not for long things.
899		 */
900			nsecsperit = ts_end.tv_sec * 1000000000;
901			nsecsperit += ts_end.tv_nsec;
902			nsecsperit /= calls;
903			printf("0.%09ju\n", (uintmax_t)nsecsperit);
904		}
905	}
906	return (0);
907}
908