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