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