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