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