]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/syscall_timing/syscall_timing.c
MFV r339792:
[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/param.h>
33 #include <sys/types.h>
34 #include <sys/mman.h>
35 #include <sys/procdesc.h>
36 #include <sys/resource.h>
37 #include <sys/socket.h>
38 #include <sys/stat.h>
39 #include <sys/time.h>
40 #include <sys/wait.h>
41
42 #include <assert.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <inttypes.h>
47 #include <limits.h>
48 #ifdef WITH_PTHREAD
49 #include <pthread.h>
50 #endif
51 #include <semaphore.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57
58 static struct timespec ts_start, ts_end;
59 static int alarm_timeout;
60 static volatile int alarm_fired;
61
62 #define BENCHMARK_FOREACH(I, NUM) for (I = 0; I < NUM && alarm_fired == 0; I++)
63
64 static void
65 alarm_handler(int signum __unused)
66 {
67
68         alarm_fired = 1;
69 }
70
71 static void
72 benchmark_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
85 static void
86 benchmark_stop(void)
87 {
88         int error;
89
90         error = clock_gettime(CLOCK_REALTIME, &ts_end);
91         assert(error == 0);
92 }
93
94 static uintmax_t
95 test_access(uintmax_t num, uintmax_t int_arg __unused, const char *path)
96 {
97         uintmax_t i;
98         int fd;
99
100         fd = access(path, O_RDONLY);
101         if (fd < 0)
102                 err(-1, "test_access: %s", path);
103         close(fd);
104
105         benchmark_start();
106         BENCHMARK_FOREACH(i, num) {
107                 access(path, O_RDONLY);
108                 close(fd);
109         }
110         benchmark_stop();
111         return (i);
112 }
113
114 static uintmax_t
115 test_bad_open(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
116 {
117         uintmax_t i;
118
119         benchmark_start();
120         BENCHMARK_FOREACH(i, num) {
121                 open("", O_RDONLY);
122         }
123         benchmark_stop();
124         return (i);
125 }
126
127 static uintmax_t
128 test_chroot(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
129 {
130         uintmax_t i;
131
132         if (chroot("/") < 0)
133                 err(-1, "test_chroot: chroot");
134         benchmark_start();
135         BENCHMARK_FOREACH(i, num) {
136                 if (chroot("/") < 0)
137                         err(-1, "test_chroot: chroot");
138         }
139         benchmark_stop();
140         return (i);
141 }
142
143 static uintmax_t
144 test_clock_gettime(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
145 {
146         struct timespec ts;
147         uintmax_t i;
148
149         benchmark_start();
150         BENCHMARK_FOREACH(i, num) {
151                 (void)clock_gettime(CLOCK_REALTIME, &ts);
152         }
153         benchmark_stop();
154         return (i);
155 }
156
157 static uintmax_t
158 test_create_unlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
159 {
160         uintmax_t i;
161         int fd;
162
163         (void)unlink(path);
164         fd = open(path, O_RDWR | O_CREAT, 0600);
165         if (fd < 0)
166                 err(-1, "test_create_unlink: create: %s", path);
167         close(fd);
168         if (unlink(path) < 0)
169                 err(-1, "test_create_unlink: unlink: %s", path);
170         benchmark_start();
171         BENCHMARK_FOREACH(i, num) {
172                 fd = open(path, O_RDWR | O_CREAT, 0600);
173                 if (fd < 0)
174                         err(-1, "test_create_unlink: create: %s", path);
175                 close(fd);
176                 if (unlink(path) < 0)
177                         err(-1, "test_create_unlink: unlink: %s", path);
178         }
179         benchmark_stop();
180         return (i);
181 }
182
183 static uintmax_t
184 test_fork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
185 {
186         pid_t pid;
187         uintmax_t i;
188
189         pid = fork();
190         if (pid < 0)
191                 err(-1, "test_fork: fork");
192         if (pid == 0)
193                 _exit(0);
194         if (waitpid(pid, NULL, 0) < 0)
195                 err(-1, "test_fork: waitpid");
196         benchmark_start();
197         BENCHMARK_FOREACH(i, num) {
198                 pid = fork();
199                 if (pid < 0)
200                         err(-1, "test_fork: fork");
201                 if (pid == 0)
202                         _exit(0);
203                 if (waitpid(pid, NULL, 0) < 0)
204                         err(-1, "test_fork: waitpid");
205         }
206         benchmark_stop();
207         return (i);
208 }
209
210 #define USR_BIN_TRUE    "/usr/bin/true"
211 static char *execve_args[] = { __DECONST(char *, USR_BIN_TRUE), NULL};
212 extern char **environ;
213
214 static uintmax_t
215 test_fork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
216 {
217         pid_t pid;
218         uintmax_t i;
219
220         pid = fork();
221         if (pid < 0)
222                 err(-1, "test_fork_exec: fork");
223         if (pid == 0) {
224                 (void)execve(USR_BIN_TRUE, execve_args, environ);
225                 err(-1, "execve");
226         }
227         if (waitpid(pid, NULL, 0) < 0)
228                 err(-1, "test_fork: waitpid");
229         benchmark_start();
230         BENCHMARK_FOREACH(i, num) {
231                 pid = fork();
232                 if (pid < 0)
233                         err(-1, "test_fork_exec: fork");
234                 if (pid == 0) {
235                         (void)execve(USR_BIN_TRUE, execve_args, environ);
236                         err(-1, "test_fork_exec: execve");
237                 }
238                 if (waitpid(pid, NULL, 0) < 0)
239                         err(-1, "test_fork_exec: waitpid");
240         }
241         benchmark_stop();
242         return (i);
243 }
244
245 static uintmax_t
246 test_getppid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
247 {
248         uintmax_t i;
249
250         /*
251          * This is process-local, but can change, so will require a
252          * lock.
253          */
254         benchmark_start();
255         BENCHMARK_FOREACH(i, num) {
256                 getppid();
257         }
258         benchmark_stop();
259         return (i);
260 }
261
262 static uintmax_t
263 test_getpriority(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
264 {
265         uintmax_t i;
266
267         benchmark_start();
268         BENCHMARK_FOREACH(i, num) {
269                 (void)getpriority(PRIO_PROCESS, 0);
270         }
271         benchmark_stop();
272         return (i);
273 }
274
275 /*
276  * The point of this one is to figure out the cost of a call into libc,
277  * through PLT, and back.
278  */
279 static uintmax_t
280 test_getprogname(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
281 {
282         uintmax_t i;
283
284         benchmark_start();
285         BENCHMARK_FOREACH(i, num) {
286                 (void)getprogname();
287         }
288         benchmark_stop();
289         return (i);
290 }
291
292 static uintmax_t
293 test_getresuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
294 {
295         uid_t ruid, euid, suid;
296         uintmax_t i;
297
298         benchmark_start();
299         BENCHMARK_FOREACH(i, num) {
300                 (void)getresuid(&ruid, &euid, &suid);
301         }
302         benchmark_stop();
303         return (i);
304 }
305
306 static uintmax_t
307 test_gettimeofday(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
308 {
309         struct timeval tv;
310         uintmax_t i;
311
312         benchmark_start();
313         BENCHMARK_FOREACH(i, num) {
314                 (void)gettimeofday(&tv, NULL);
315         }
316         benchmark_stop();
317         return (i);
318 }
319
320 static uintmax_t
321 test_getuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
322 {
323         uintmax_t i;
324
325         /*
326          * Thread-local data should require no locking if system
327          * call is MPSAFE.
328          */
329         benchmark_start();
330         BENCHMARK_FOREACH(i, num) {
331                 getuid();
332         }
333         benchmark_stop();
334         return (i);
335 }
336
337 static uintmax_t
338 test_memcpy(uintmax_t num, uintmax_t int_arg, const char *path __unused)
339 {
340         char buf[int_arg], buf2[int_arg];
341         uintmax_t i;
342
343         benchmark_start();
344         BENCHMARK_FOREACH(i, num) {
345                 /*
346                  * Copy the memory there and back, to match the total amount
347                  * moved by pipeping/pipepingtd tests.
348                  */
349                 memcpy(buf2, buf, int_arg);
350                 memcpy(buf, buf2, int_arg);
351         }
352         benchmark_stop();
353
354         return (i);
355 }
356
357 static uintmax_t
358 test_open_close(uintmax_t num, uintmax_t int_arg __unused, const char *path)
359 {
360         uintmax_t i;
361         int fd;
362
363         fd = open(path, O_RDONLY);
364         if (fd < 0)
365                 err(-1, "test_open_close: %s", path);
366         close(fd);
367
368         benchmark_start();
369         BENCHMARK_FOREACH(i, num) {
370                 fd = open(path, O_RDONLY);
371                 if (fd < 0)
372                         err(-1, "test_open_close: %s", path);
373                 close(fd);
374         }
375         benchmark_stop();
376         return (i);
377 }
378
379 static uintmax_t
380 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
381 {
382         char buf[int_arg];
383         uintmax_t i;
384         int fd;
385
386         fd = open(path, O_RDONLY);
387         if (fd < 0)
388                 err(-1, "test_open_read_close: %s", path);
389         (void)read(fd, buf, int_arg);
390         close(fd);
391
392         benchmark_start();
393         BENCHMARK_FOREACH(i, num) {
394                 fd = open(path, O_RDONLY);
395                 if (fd < 0)
396                         err(-1, "test_open_read_close: %s", path);
397                 (void)read(fd, buf, int_arg);
398                 close(fd);
399         }
400         benchmark_stop();
401         return (i);
402 }
403
404 static uintmax_t
405 test_pipe(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
406 {
407         int fd[2];
408         uintmax_t i;
409
410         /*
411          * pipe creation is expensive, as it will allocate a new file
412          * descriptor, allocate a new pipe, hook it all up, and return.
413          * Destroying is also expensive, as we now have to free up
414          * the file descriptors and return the pipe.
415          */
416         if (pipe(fd) < 0)
417                 err(-1, "test_pipe: pipe");
418         close(fd[0]);
419         close(fd[1]);
420         benchmark_start();
421         BENCHMARK_FOREACH(i, num) {
422                 if (pipe(fd) == -1)
423                         err(-1, "test_pipe: pipe");
424                 close(fd[0]);
425                 close(fd[1]);
426         }
427         benchmark_stop();
428         return (i);
429 }
430
431 static void
432 readx(int fd, char *buf, size_t size)
433 {
434         ssize_t ret;
435
436         do {
437                 ret = read(fd, buf, size);
438                 if (ret == -1)
439                         err(1, "read");
440                 assert((size_t)ret <= size);
441                 size -= ret;
442                 buf += ret;
443         } while (size > 0);
444 }
445
446 static void
447 writex(int fd, const char *buf, size_t size)
448 {
449         ssize_t ret;
450
451         do {
452                 ret = write(fd, buf, size);
453                 if (ret == -1)
454                         err(1, "write");
455                 assert((size_t)ret <= size);
456                 size -= ret;
457                 buf += ret;
458         } while (size > 0);
459 }
460
461 static uintmax_t
462 test_pipeping(uintmax_t num, uintmax_t int_arg, const char *path __unused)
463 {
464         char buf[int_arg];
465         uintmax_t i;
466         pid_t pid;
467         int fd[2], procfd;
468
469         if (pipe(fd) < 0)
470                 err(-1, "pipe");
471
472         pid = pdfork(&procfd, 0);
473         if (pid < 0)
474                 err(1, "pdfork");
475
476         if (pid == 0) {
477                 close(fd[0]);
478
479                 for (;;) {
480                         readx(fd[1], buf, int_arg);
481                         writex(fd[1], buf, int_arg);
482                 }
483         }
484
485         close(fd[1]);
486
487         benchmark_start();
488         BENCHMARK_FOREACH(i, num) {
489                 writex(fd[0], buf, int_arg);
490                 readx(fd[0], buf, int_arg);
491         }
492         benchmark_stop();
493
494         close(procfd);
495         return (i);
496 }
497
498 #ifdef WITH_PTHREAD
499 struct pipepingtd_ctx {
500         int             fd;
501         uintmax_t       int_arg;
502 };
503
504 static void *
505 pipepingtd_proc(void *arg)
506 {
507         struct pipepingtd_ctx *ctxp;
508         int fd;
509         void *buf;
510         uintmax_t int_arg;
511
512         ctxp = arg;
513         fd = ctxp->fd;
514         int_arg = ctxp->int_arg;
515
516         buf = malloc(int_arg);
517         if (buf == NULL)
518                 err(1, "malloc");
519
520         for (;;) {
521                 readx(fd, buf, int_arg);
522                 writex(fd, buf, int_arg);
523         }
524 }
525
526 static uintmax_t
527 test_pipepingtd(uintmax_t num, uintmax_t int_arg, const char *path __unused)
528 {
529         struct pipepingtd_ctx ctx;
530         char buf[int_arg];
531         pthread_t td;
532         uintmax_t i;
533         int error, fd[2];
534
535         if (pipe(fd) < 0)
536                 err(-1, "pipe");
537
538         ctx.fd = fd[1];
539         ctx.int_arg = int_arg;
540
541         error = pthread_create(&td, NULL, pipepingtd_proc, &ctx);
542         if (error != 0)
543                 err(1, "pthread_create");
544
545         benchmark_start();
546         BENCHMARK_FOREACH(i, num) {
547                 writex(fd[0], buf, int_arg);
548                 readx(fd[0], buf, int_arg);
549         }
550         benchmark_stop();
551         pthread_cancel(td);
552
553         return (i);
554 }
555 #endif /* WITH_PTHREAD */
556
557 static uintmax_t
558 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
559 {
560         char buf[int_arg];
561         uintmax_t i;
562         int fd;
563
564         fd = open(path, O_RDONLY);
565         if (fd < 0)
566                 err(-1, "test_open_read: %s", path);
567         (void)pread(fd, buf, int_arg, 0);
568
569         benchmark_start();
570         BENCHMARK_FOREACH(i, num) {
571                 (void)pread(fd, buf, int_arg, 0);
572         }
573         benchmark_stop();
574         close(fd);
575         return (i);
576 }
577
578 static uintmax_t
579 test_select(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
580 {
581         fd_set readfds, writefds, exceptfds;
582         struct timeval tv;
583         uintmax_t i;
584
585         FD_ZERO(&readfds);
586         FD_ZERO(&writefds);
587         FD_ZERO(&exceptfds);
588
589         tv.tv_sec = 0;
590         tv.tv_usec = 0;
591
592         benchmark_start();
593         BENCHMARK_FOREACH(i, num) {
594                 (void)select(0, &readfds, &writefds, &exceptfds, &tv);
595         }
596         benchmark_stop();
597         return (i);
598 }
599
600 static uintmax_t
601 test_semaping(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
602 {
603         uintmax_t i;
604         pid_t pid;
605         sem_t *buf;
606         int error, j, procfd;
607
608         buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
609         if (buf == MAP_FAILED)
610                 err(1, "mmap");
611
612         for (j = 0; j < 2; j++) {
613                 error = sem_init(&buf[j], 1, 0);
614                 if (error != 0)
615                         err(1, "sem_init");
616         }
617
618         pid = pdfork(&procfd, 0);
619         if (pid < 0)
620                 err(1, "pdfork");
621
622         if (pid == 0) {
623                 for (;;) {
624                         error = sem_wait(&buf[0]);
625                         if (error != 0)
626                                 err(1, "sem_wait");
627                         error = sem_post(&buf[1]);
628                         if (error != 0)
629                                 err(1, "sem_post");
630                 }
631         }
632
633         benchmark_start();
634         BENCHMARK_FOREACH(i, num) {
635                 error = sem_post(&buf[0]);
636                 if (error != 0)
637                         err(1, "sem_post");
638                 error = sem_wait(&buf[1]);
639                 if (error != 0)
640                         err(1, "sem_wait");
641         }
642         benchmark_stop();
643
644         close(procfd);
645
646         for (j = 0; j < 2; j++) {
647                 error = sem_destroy(&buf[j]);
648                 if (error != 0)
649                         err(1, "sem_destroy");
650         }
651
652         error = munmap(buf, PAGE_SIZE);
653         if (error != 0)
654                 err(1, "munmap");
655
656         return (i);
657 }
658
659 static uintmax_t
660 test_setuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
661 {
662         uid_t uid;
663         uintmax_t i;
664
665         uid = getuid();
666         if (setuid(uid) < 0)
667                 err(-1, "test_setuid: setuid");
668         benchmark_start();
669         BENCHMARK_FOREACH(i, num) {
670                 if (setuid(uid) < 0)
671                         err(-1, "test_setuid: setuid");
672         }
673         benchmark_stop();
674         return (i);
675 }
676
677 static uintmax_t
678 test_shmfd(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
679 {
680         uintmax_t i;
681         int shmfd;
682
683         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
684         if (shmfd < 0)
685                 err(-1, "test_shmfd: shm_open");
686         close(shmfd);
687         benchmark_start();
688         BENCHMARK_FOREACH(i, num) {
689                 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
690                 if (shmfd < 0)
691                         err(-1, "test_shmfd: shm_open");
692                 close(shmfd);
693         }
694         benchmark_stop();
695         return (i);
696 }
697
698 static uintmax_t
699 test_shmfd_dup(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
700 {
701         uintmax_t i;
702         int fd, shmfd;
703
704         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
705         if (shmfd < 0)
706                 err(-1, "test_shmfd_dup: shm_open");
707         fd = dup(shmfd);
708         if (fd >= 0)
709                 close(fd);
710         benchmark_start();
711         BENCHMARK_FOREACH(i, num) {
712                 fd = dup(shmfd);
713                 if (fd >= 0)
714                         close(fd);
715         }
716         benchmark_stop();
717         close(shmfd);
718         return (i);
719 }
720
721 static uintmax_t
722 test_shmfd_fstat(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
723 {
724         struct stat sb;
725         uintmax_t i;
726         int shmfd;
727
728         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
729         if (shmfd < 0)
730                 err(-1, "test_shmfd_fstat: shm_open");
731         if (fstat(shmfd, &sb) < 0)
732                 err(-1, "test_shmfd_fstat: fstat");
733         benchmark_start();
734         BENCHMARK_FOREACH(i, num) {
735                 (void)fstat(shmfd, &sb);
736         }
737         benchmark_stop();
738         close(shmfd);
739         return (i);
740 }
741
742 static uintmax_t
743 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path __unused)
744 {
745         uintmax_t i;
746         int so;
747
748         so = socket(int_arg, SOCK_STREAM, 0);
749         if (so < 0)
750                 err(-1, "test_socket_stream: socket");
751         close(so);
752         benchmark_start();
753         BENCHMARK_FOREACH(i, num) {
754                 so = socket(int_arg, SOCK_STREAM, 0);
755                 if (so == -1)
756                         err(-1, "test_socket_stream: socket");
757                 close(so);
758         }
759         benchmark_stop();
760         return (i);
761 }
762
763 static uintmax_t
764 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path __unused)
765 {
766         uintmax_t i;
767         int so;
768
769         so = socket(int_arg, SOCK_DGRAM, 0);
770         if (so < 0)
771                 err(-1, "test_socket_dgram: socket");
772         close(so);
773         benchmark_start();
774         BENCHMARK_FOREACH(i, num) {
775                 so = socket(int_arg, SOCK_DGRAM, 0);
776                 if (so == -1)
777                         err(-1, "test_socket_dgram: socket");
778                 close(so);
779         }
780         benchmark_stop();
781         return (i);
782 }
783
784 static uintmax_t
785 test_socketpair_stream(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
786 {
787         uintmax_t i;
788         int so[2];
789
790         if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
791                 err(-1, "test_socketpair_stream: socketpair");
792         close(so[0]);
793         close(so[1]);
794         benchmark_start();
795         BENCHMARK_FOREACH(i, num) {
796                 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
797                         err(-1, "test_socketpair_stream: socketpair");
798                 close(so[0]);
799                 close(so[1]);
800         }
801         benchmark_stop();
802         return (i);
803 }
804
805 static uintmax_t
806 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
807 {
808         uintmax_t i;
809         int so[2];
810
811         if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
812                 err(-1, "test_socketpair_dgram: socketpair");
813         close(so[0]);
814         close(so[1]);
815         benchmark_start();
816         BENCHMARK_FOREACH(i, num) {
817                 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
818                         err(-1, "test_socketpair_dgram: socketpair");
819                 close(so[0]);
820                 close(so[1]);
821         }
822         benchmark_stop();
823         return (i);
824 }
825
826 static uintmax_t
827 test_vfork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
828 {
829         pid_t pid;
830         uintmax_t i;
831
832         pid = vfork();
833         if (pid < 0)
834                 err(-1, "test_vfork: vfork");
835         if (pid == 0)
836                 _exit(0);
837         if (waitpid(pid, NULL, 0) < 0)
838                 err(-1, "test_vfork: waitpid");
839         benchmark_start();
840         BENCHMARK_FOREACH(i, num) {
841                 pid = vfork();
842                 if (pid < 0)
843                         err(-1, "test_vfork: vfork");
844                 if (pid == 0)
845                         _exit(0);
846                 if (waitpid(pid, NULL, 0) < 0)
847                         err(-1, "test_vfork: waitpid");
848         }
849         benchmark_stop();
850         return (i);
851 }
852
853 static uintmax_t
854 test_vfork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
855 {
856         pid_t pid;
857         uintmax_t i;
858
859         pid = vfork();
860         if (pid < 0)
861                 err(-1, "test_vfork_exec: vfork");
862         if (pid == 0) {
863                 (void)execve(USR_BIN_TRUE, execve_args, environ);
864                 err(-1, "test_vfork_exec: execve");
865         }
866         if (waitpid(pid, NULL, 0) < 0)
867                 err(-1, "test_vfork_exec: waitpid");
868         benchmark_start();
869         BENCHMARK_FOREACH(i, num) {
870                 pid = vfork();
871                 if (pid < 0)
872                         err(-1, "test_vfork_exec: vfork");
873                 if (pid == 0) {
874                         (void)execve(USR_BIN_TRUE, execve_args, environ);
875                         err(-1, "execve");
876                 }
877                 if (waitpid(pid, NULL, 0) < 0)
878                         err(-1, "test_vfork_exec: waitpid");
879         }
880         benchmark_stop();
881         return (i);
882 }
883
884 struct test {
885         const char      *t_name;
886         uintmax_t       (*t_func)(uintmax_t, uintmax_t, const char *);
887         int              t_flags;
888         uintmax_t        t_int;
889 };
890
891 #define FLAG_PATH       0x00000001
892
893 static const struct test tests[] = {
894         { "access", test_access, .t_flags = FLAG_PATH },
895         { "bad_open", test_bad_open, .t_flags = 0 },
896         { "chroot", test_chroot, .t_flags = 0 },
897         { "clock_gettime", test_clock_gettime, .t_flags = 0 },
898         { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
899         { "fork", test_fork, .t_flags = 0 },
900         { "fork_exec", test_fork_exec, .t_flags = 0 },
901         { "getppid", test_getppid, .t_flags = 0 },
902         { "getpriority", test_getpriority, .t_flags = 0 },
903         { "getprogname", test_getprogname, .t_flags = 0 },
904         { "getresuid", test_getresuid, .t_flags = 0 },
905         { "gettimeofday", test_gettimeofday, .t_flags = 0 },
906         { "getuid", test_getuid, .t_flags = 0 },
907         { "memcpy_1", test_memcpy, .t_flags = 0, .t_int = 1 },
908         { "memcpy_10", test_memcpy, .t_flags = 0, .t_int = 10 },
909         { "memcpy_100", test_memcpy, .t_flags = 0, .t_int = 100 },
910         { "memcpy_1000", test_memcpy, .t_flags = 0, .t_int = 1000 },
911         { "memcpy_10000", test_memcpy, .t_flags = 0, .t_int = 10000 },
912         { "memcpy_100000", test_memcpy, .t_flags = 0, .t_int = 100000 },
913         { "memcpy_1000000", test_memcpy, .t_flags = 0, .t_int = 1000000 },
914         { "open_close", test_open_close, .t_flags = FLAG_PATH },
915         { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
916             .t_int = 1 },
917         { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
918             .t_int = 10 },
919         { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
920             .t_int = 100 },
921         { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
922             .t_int = 1000 },
923         { "open_read_close_10000", test_open_read_close,
924             .t_flags = FLAG_PATH, .t_int = 10000 },
925         { "open_read_close_100000", test_open_read_close,
926             .t_flags = FLAG_PATH, .t_int = 100000 },
927         { "open_read_close_1000000", test_open_read_close,
928             .t_flags = FLAG_PATH, .t_int = 1000000 },
929         { "pipe", test_pipe, .t_flags = 0 },
930         { "pipeping_1", test_pipeping, .t_flags = 0, .t_int = 1 },
931         { "pipeping_10", test_pipeping, .t_flags = 0, .t_int = 10 },
932         { "pipeping_100", test_pipeping, .t_flags = 0, .t_int = 100 },
933         { "pipeping_1000", test_pipeping, .t_flags = 0, .t_int = 1000 },
934         { "pipeping_10000", test_pipeping, .t_flags = 0, .t_int = 10000 },
935         { "pipeping_100000", test_pipeping, .t_flags = 0, .t_int = 100000 },
936         { "pipeping_1000000", test_pipeping, .t_flags = 0, .t_int = 1000000 },
937 #ifdef WITH_PTHREAD
938         { "pipepingtd_1", test_pipepingtd, .t_flags = 0, .t_int = 1 },
939         { "pipepingtd_10", test_pipepingtd, .t_flags = 0, .t_int = 10 },
940         { "pipepingtd_100", test_pipepingtd, .t_flags = 0, .t_int = 100 },
941         { "pipepingtd_1000", test_pipepingtd, .t_flags = 0, .t_int = 1000 },
942         { "pipepingtd_10000", test_pipepingtd, .t_flags = 0, .t_int = 10000 },
943         { "pipepingtd_100000", test_pipepingtd, .t_flags = 0, .t_int = 100000 },
944         { "pipepingtd_1000000", test_pipepingtd, .t_flags = 0, .t_int = 1000000 },
945 #endif
946         { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
947         { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
948         { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
949         { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
950         { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
951         { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
952         { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
953         { "select", test_select, .t_flags = 0 },
954         { "semaping", test_semaping, .t_flags = 0 },
955         { "setuid", test_setuid, .t_flags = 0 },
956         { "shmfd", test_shmfd, .t_flags = 0 },
957         { "shmfd_dup", test_shmfd_dup, .t_flags = 0 },
958         { "shmfd_fstat", test_shmfd_fstat, .t_flags = 0 },
959         { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
960         { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
961         { "socketpair_stream", test_socketpair_stream, .t_flags = 0 },
962         { "socketpair_dgram", test_socketpair_dgram, .t_flags = 0 },
963         { "socket_tcp", test_socket_stream, .t_int = PF_INET },
964         { "socket_udp", test_socket_dgram, .t_int = PF_INET },
965         { "vfork", test_vfork, .t_flags = 0 },
966         { "vfork_exec", test_vfork_exec, .t_flags = 0 },
967 };
968 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
969
970 static void
971 usage(void)
972 {
973         int i;
974
975         fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
976             "[-p path] [-s seconds] test\n");
977         for (i = 0; i < tests_count; i++)
978                 fprintf(stderr, "  %s\n", tests[i].t_name);
979         exit(-1);
980 }
981
982 int
983 main(int argc, char *argv[])
984 {
985         struct timespec ts_res;
986         const struct test *the_test;
987         const char *path;
988         char *tmp_dir, *tmp_path;
989         long long ll;
990         char *endp;
991         int ch, fd, error, i, j, rv;
992         uintmax_t iterations, k, loops;
993
994         alarm_timeout = 1;
995         iterations = 0;
996         loops = 10;
997         path = NULL;
998         tmp_path = NULL;
999         while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
1000                 switch (ch) {
1001                 case 'i':
1002                         ll = strtol(optarg, &endp, 10);
1003                         if (*endp != 0 || ll < 1)
1004                                 usage();
1005                         iterations = ll;
1006                         break;
1007
1008                 case 'l':
1009                         ll = strtol(optarg, &endp, 10);
1010                         if (*endp != 0 || ll < 1 || ll > 100000)
1011                                 usage();
1012                         loops = ll;
1013                         break;
1014
1015                 case 'p':
1016                         path = optarg;
1017                         break;
1018
1019                 case 's':
1020                         ll = strtol(optarg, &endp, 10);
1021                         if (*endp != 0 || ll < 1 || ll > 60*60)
1022                                 usage();
1023                         alarm_timeout = ll;
1024                         break;
1025
1026                 case '?':
1027                 default:
1028                         usage();
1029                 }
1030         }
1031         argc -= optind;
1032         argv += optind;
1033
1034         if (iterations < 1 && alarm_timeout < 1)
1035                 usage();
1036         if (iterations < 1)
1037                 iterations = UINT64_MAX;
1038         if (loops < 1)
1039                 loops = 1;
1040
1041         if (argc < 1)
1042                 usage();
1043
1044         /*
1045          * Validate test list and that, if a path is required, it is
1046          * defined.
1047          */
1048         for (j = 0; j < argc; j++) {
1049                 the_test = NULL;
1050                 for (i = 0; i < tests_count; i++) {
1051                         if (strcmp(argv[j], tests[i].t_name) == 0)
1052                                 the_test = &tests[i];
1053                 }
1054                 if (the_test == NULL)
1055                         usage();
1056                 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
1057                         tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
1058                         if (tmp_dir == NULL)
1059                                 err(1, "strdup");
1060                         tmp_dir = mkdtemp(tmp_dir);
1061                         if (tmp_dir == NULL)
1062                                 err(1, "mkdtemp");
1063                         rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
1064                         if (rv <= 0)
1065                                 err(1, "asprintf");
1066                 }
1067         }
1068
1069         error = clock_getres(CLOCK_REALTIME, &ts_res);
1070         assert(error == 0);
1071         printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
1072             (uintmax_t)ts_res.tv_nsec);
1073         printf("test\tloop\ttime\titerations\tperiteration\n");
1074
1075         for (j = 0; j < argc; j++) {
1076                 uintmax_t calls, nsecsperit;
1077
1078                 the_test = NULL;
1079                 for (i = 0; i < tests_count; i++) {
1080                         if (strcmp(argv[j], tests[i].t_name) == 0)
1081                                 the_test = &tests[i];
1082                 }
1083
1084                 if (tmp_path != NULL) {
1085                         fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
1086                         if (fd < 0)
1087                                 err(1, "cannot open %s", tmp_path);
1088                         error = ftruncate(fd, 1000000);
1089                         if (error != 0)
1090                                 err(1, "ftruncate");
1091                         error = close(fd);
1092                         if (error != 0)
1093                                 err(1, "close");
1094                         path = tmp_path;
1095                 }
1096
1097                 /*
1098                  * Run one warmup, then do the real thing (loops) times.
1099                  */
1100                 the_test->t_func(iterations, the_test->t_int, path);
1101                 calls = 0;
1102                 for (k = 0; k < loops; k++) {
1103                         calls = the_test->t_func(iterations, the_test->t_int,
1104                             path);
1105                         timespecsub(&ts_end, &ts_start, &ts_end);
1106                         printf("%s\t%ju\t", the_test->t_name, k);
1107                         printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
1108                             (uintmax_t)ts_end.tv_nsec, calls);
1109
1110                 /*
1111                  * Note.  This assumes that each iteration takes less than
1112                  * a second, and that our total nanoseconds doesn't exceed
1113                  * the room in our arithmetic unit.  Fine for system calls,
1114                  * but not for long things.
1115                  */
1116                         nsecsperit = ts_end.tv_sec * 1000000000;
1117                         nsecsperit += ts_end.tv_nsec;
1118                         nsecsperit /= calls;
1119                         printf("0.%09ju\n", (uintmax_t)nsecsperit);
1120                 }
1121         }
1122
1123         if (tmp_path != NULL) {
1124                 error = unlink(tmp_path);
1125                 if (error != 0 && errno != ENOENT)
1126                         warn("cannot unlink %s", tmp_path);
1127                 error = rmdir(tmp_dir);
1128                 if (error != 0)
1129                         warn("cannot rmdir %s", tmp_dir);
1130         }
1131
1132         return (0);
1133 }