]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/syscall_timing/syscall_timing.c
Merge ^/head r327341 through r327623.
[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_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
150 uintmax_t
151 test_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
166 uintmax_t
167 test_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
182 uintmax_t
183 test_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
197 uintmax_t
198 test_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
225 uintmax_t
226 test_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
250 uintmax_t
251 test_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
273 uintmax_t
274 test_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
296 uintmax_t
297 test_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
319 uintmax_t
320 test_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
342 uintmax_t
343 test_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
364 uintmax_t
365 test_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
392 uintmax_t
393 test_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
416 uintmax_t
417 test_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
431 uintmax_t
432 test_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
454 uintmax_t
455 test_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
481 uintmax_t
482 test_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
505 uintmax_t
506 test_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
528 uintmax_t
529 test_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
551 uintmax_t
552 test_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
580 uintmax_t
581 test_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"
610 static char *execve_args[] = { USR_BIN_TRUE, NULL};
611 extern char **environ;
612
613 uintmax_t
614 test_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
646 uintmax_t
647 test_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
679 uintmax_t
680 test_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
697 uintmax_t
698 test_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
717 struct 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
726 static 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 };
776 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
777
778 static void
779 usage(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
790 int
791 main(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 }