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
50static struct timespec ts_start, ts_end;
51static int alarm_timeout;
52static 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
64static void
65alarm_handler(int signum)
66{
67
68	alarm_fired = 1;
69}
70
71static void
72benchmark_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
85static void
86benchmark_stop(void)
87{
88	int error;
89
90	error = clock_gettime(CLOCK_REALTIME, &ts_end);
91	assert(error == 0);
92}
93
94uintmax_t
95test_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
113uintmax_t
114test_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
132uintmax_t
133test_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
148uintmax_t
149test_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
164uintmax_t
165test_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
192uintmax_t
193test_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
215uintmax_t
216test_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
238uintmax_t
239test_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
261uintmax_t
262test_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
284uintmax_t
285test_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
312uintmax_t
313test_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
336uintmax_t
337test_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
359uintmax_t
360test_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
386uintmax_t
387test_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
410uintmax_t
411test_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
433uintmax_t
434test_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
456uintmax_t
457test_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
485uintmax_t
486test_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"
515static char *execve_args[] = { USR_BIN_TRUE, NULL};
516extern char **environ;
517
518uintmax_t
519test_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
551uintmax_t
552test_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
584uintmax_t
585test_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
602uintmax_t
603test_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
622struct 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
631static 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};
676static const int tests_count = sizeof(tests) / sizeof(tests[0]);
677
678static void
679usage(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
690int
691main(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