]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/syscall_timing/syscall_timing.c
Do a sweep and remove most WARNS=6 settings
[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_lstat(uintmax_t num, uintmax_t int_arg __unused, const char *path)
339 {
340         struct stat sb;
341         uintmax_t i;
342         int error;
343
344         benchmark_start();
345         BENCHMARK_FOREACH(i, num) {
346                 error = lstat(path, &sb);
347                 if (error != 0)
348                         err(-1, "lstat");
349         }
350         benchmark_stop();
351         return (i);
352 }
353
354 static uintmax_t
355 test_memcpy(uintmax_t num, uintmax_t int_arg, const char *path __unused)
356 {
357         char buf[int_arg], buf2[int_arg];
358         uintmax_t i;
359
360         benchmark_start();
361         BENCHMARK_FOREACH(i, num) {
362                 /*
363                  * Copy the memory there and back, to match the total amount
364                  * moved by pipeping/pipepingtd tests.
365                  */
366                 memcpy(buf2, buf, int_arg);
367                 memcpy(buf, buf2, int_arg);
368         }
369         benchmark_stop();
370
371         return (i);
372 }
373
374 static uintmax_t
375 test_open_close(uintmax_t num, uintmax_t int_arg __unused, const char *path)
376 {
377         uintmax_t i;
378         int fd;
379
380         fd = open(path, O_RDONLY);
381         if (fd < 0)
382                 err(-1, "test_open_close: %s", path);
383         close(fd);
384
385         benchmark_start();
386         BENCHMARK_FOREACH(i, num) {
387                 fd = open(path, O_RDONLY);
388                 if (fd < 0)
389                         err(-1, "test_open_close: %s", path);
390                 close(fd);
391         }
392         benchmark_stop();
393         return (i);
394 }
395
396 static uintmax_t
397 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
398 {
399         char buf[int_arg];
400         uintmax_t i;
401         int fd;
402
403         fd = open(path, O_RDONLY);
404         if (fd < 0)
405                 err(-1, "test_open_read_close: %s", path);
406         (void)read(fd, buf, int_arg);
407         close(fd);
408
409         benchmark_start();
410         BENCHMARK_FOREACH(i, num) {
411                 fd = open(path, O_RDONLY);
412                 if (fd < 0)
413                         err(-1, "test_open_read_close: %s", path);
414                 (void)read(fd, buf, int_arg);
415                 close(fd);
416         }
417         benchmark_stop();
418         return (i);
419 }
420
421 static uintmax_t
422 test_pipe(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
423 {
424         int fd[2];
425         uintmax_t i;
426
427         /*
428          * pipe creation is expensive, as it will allocate a new file
429          * descriptor, allocate a new pipe, hook it all up, and return.
430          * Destroying is also expensive, as we now have to free up
431          * the file descriptors and return the pipe.
432          */
433         if (pipe(fd) < 0)
434                 err(-1, "test_pipe: pipe");
435         close(fd[0]);
436         close(fd[1]);
437         benchmark_start();
438         BENCHMARK_FOREACH(i, num) {
439                 if (pipe(fd) == -1)
440                         err(-1, "test_pipe: pipe");
441                 close(fd[0]);
442                 close(fd[1]);
443         }
444         benchmark_stop();
445         return (i);
446 }
447
448 static void
449 readx(int fd, char *buf, size_t size)
450 {
451         ssize_t ret;
452
453         do {
454                 ret = read(fd, buf, size);
455                 if (ret == -1)
456                         err(1, "read");
457                 assert((size_t)ret <= size);
458                 size -= ret;
459                 buf += ret;
460         } while (size > 0);
461 }
462
463 static void
464 writex(int fd, const char *buf, size_t size)
465 {
466         ssize_t ret;
467
468         do {
469                 ret = write(fd, buf, size);
470                 if (ret == -1)
471                         err(1, "write");
472                 assert((size_t)ret <= size);
473                 size -= ret;
474                 buf += ret;
475         } while (size > 0);
476 }
477
478 static uintmax_t
479 test_pipeping(uintmax_t num, uintmax_t int_arg, const char *path __unused)
480 {
481         char buf[int_arg];
482         uintmax_t i;
483         pid_t pid;
484         int fd[2], procfd;
485
486         if (pipe(fd) < 0)
487                 err(-1, "pipe");
488
489         pid = pdfork(&procfd, 0);
490         if (pid < 0)
491                 err(1, "pdfork");
492
493         if (pid == 0) {
494                 close(fd[0]);
495
496                 for (;;) {
497                         readx(fd[1], buf, int_arg);
498                         writex(fd[1], buf, int_arg);
499                 }
500         }
501
502         close(fd[1]);
503
504         benchmark_start();
505         BENCHMARK_FOREACH(i, num) {
506                 writex(fd[0], buf, int_arg);
507                 readx(fd[0], buf, int_arg);
508         }
509         benchmark_stop();
510
511         close(procfd);
512         return (i);
513 }
514
515 #ifdef WITH_PTHREAD
516 struct pipepingtd_ctx {
517         int             fd;
518         uintmax_t       int_arg;
519 };
520
521 static void *
522 pipepingtd_proc(void *arg)
523 {
524         struct pipepingtd_ctx *ctxp;
525         int fd;
526         void *buf;
527         uintmax_t int_arg;
528
529         ctxp = arg;
530         fd = ctxp->fd;
531         int_arg = ctxp->int_arg;
532
533         buf = malloc(int_arg);
534         if (buf == NULL)
535                 err(1, "malloc");
536
537         for (;;) {
538                 readx(fd, buf, int_arg);
539                 writex(fd, buf, int_arg);
540         }
541 }
542
543 static uintmax_t
544 test_pipepingtd(uintmax_t num, uintmax_t int_arg, const char *path __unused)
545 {
546         struct pipepingtd_ctx ctx;
547         char buf[int_arg];
548         pthread_t td;
549         uintmax_t i;
550         int error, fd[2];
551
552         if (pipe(fd) < 0)
553                 err(-1, "pipe");
554
555         ctx.fd = fd[1];
556         ctx.int_arg = int_arg;
557
558         error = pthread_create(&td, NULL, pipepingtd_proc, &ctx);
559         if (error != 0)
560                 err(1, "pthread_create");
561
562         benchmark_start();
563         BENCHMARK_FOREACH(i, num) {
564                 writex(fd[0], buf, int_arg);
565                 readx(fd[0], buf, int_arg);
566         }
567         benchmark_stop();
568         pthread_cancel(td);
569
570         return (i);
571 }
572 #endif /* WITH_PTHREAD */
573
574 static uintmax_t
575 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
576 {
577         char buf[int_arg];
578         uintmax_t i;
579         int fd;
580
581         fd = open(path, O_RDONLY);
582         if (fd < 0)
583                 err(-1, "test_open_read: %s", path);
584         (void)pread(fd, buf, int_arg, 0);
585
586         benchmark_start();
587         BENCHMARK_FOREACH(i, num) {
588                 (void)pread(fd, buf, int_arg, 0);
589         }
590         benchmark_stop();
591         close(fd);
592         return (i);
593 }
594
595 static uintmax_t
596 test_select(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
597 {
598         fd_set readfds, writefds, exceptfds;
599         struct timeval tv;
600         uintmax_t i;
601
602         FD_ZERO(&readfds);
603         FD_ZERO(&writefds);
604         FD_ZERO(&exceptfds);
605
606         tv.tv_sec = 0;
607         tv.tv_usec = 0;
608
609         benchmark_start();
610         BENCHMARK_FOREACH(i, num) {
611                 (void)select(0, &readfds, &writefds, &exceptfds, &tv);
612         }
613         benchmark_stop();
614         return (i);
615 }
616
617 static uintmax_t
618 test_semaping(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
619 {
620         uintmax_t i;
621         pid_t pid;
622         sem_t *buf;
623         int error, j, procfd;
624
625         buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
626         if (buf == MAP_FAILED)
627                 err(1, "mmap");
628
629         for (j = 0; j < 2; j++) {
630                 error = sem_init(&buf[j], 1, 0);
631                 if (error != 0)
632                         err(1, "sem_init");
633         }
634
635         pid = pdfork(&procfd, 0);
636         if (pid < 0)
637                 err(1, "pdfork");
638
639         if (pid == 0) {
640                 for (;;) {
641                         error = sem_wait(&buf[0]);
642                         if (error != 0)
643                                 err(1, "sem_wait");
644                         error = sem_post(&buf[1]);
645                         if (error != 0)
646                                 err(1, "sem_post");
647                 }
648         }
649
650         benchmark_start();
651         BENCHMARK_FOREACH(i, num) {
652                 error = sem_post(&buf[0]);
653                 if (error != 0)
654                         err(1, "sem_post");
655                 error = sem_wait(&buf[1]);
656                 if (error != 0)
657                         err(1, "sem_wait");
658         }
659         benchmark_stop();
660
661         close(procfd);
662
663         for (j = 0; j < 2; j++) {
664                 error = sem_destroy(&buf[j]);
665                 if (error != 0)
666                         err(1, "sem_destroy");
667         }
668
669         error = munmap(buf, PAGE_SIZE);
670         if (error != 0)
671                 err(1, "munmap");
672
673         return (i);
674 }
675
676 static uintmax_t
677 test_setuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
678 {
679         uid_t uid;
680         uintmax_t i;
681
682         uid = getuid();
683         if (setuid(uid) < 0)
684                 err(-1, "test_setuid: setuid");
685         benchmark_start();
686         BENCHMARK_FOREACH(i, num) {
687                 if (setuid(uid) < 0)
688                         err(-1, "test_setuid: setuid");
689         }
690         benchmark_stop();
691         return (i);
692 }
693
694 static uintmax_t
695 test_shmfd(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
696 {
697         uintmax_t i;
698         int shmfd;
699
700         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
701         if (shmfd < 0)
702                 err(-1, "test_shmfd: shm_open");
703         close(shmfd);
704         benchmark_start();
705         BENCHMARK_FOREACH(i, num) {
706                 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
707                 if (shmfd < 0)
708                         err(-1, "test_shmfd: shm_open");
709                 close(shmfd);
710         }
711         benchmark_stop();
712         return (i);
713 }
714
715 static uintmax_t
716 test_shmfd_dup(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
717 {
718         uintmax_t i;
719         int fd, shmfd;
720
721         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
722         if (shmfd < 0)
723                 err(-1, "test_shmfd_dup: shm_open");
724         fd = dup(shmfd);
725         if (fd >= 0)
726                 close(fd);
727         benchmark_start();
728         BENCHMARK_FOREACH(i, num) {
729                 fd = dup(shmfd);
730                 if (fd >= 0)
731                         close(fd);
732         }
733         benchmark_stop();
734         close(shmfd);
735         return (i);
736 }
737
738 static uintmax_t
739 test_shmfd_fstat(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
740 {
741         struct stat sb;
742         uintmax_t i;
743         int shmfd;
744
745         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
746         if (shmfd < 0)
747                 err(-1, "test_shmfd_fstat: shm_open");
748         if (fstat(shmfd, &sb) < 0)
749                 err(-1, "test_shmfd_fstat: fstat");
750         benchmark_start();
751         BENCHMARK_FOREACH(i, num) {
752                 (void)fstat(shmfd, &sb);
753         }
754         benchmark_stop();
755         close(shmfd);
756         return (i);
757 }
758
759 static uintmax_t
760 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path __unused)
761 {
762         uintmax_t i;
763         int so;
764
765         so = socket(int_arg, SOCK_STREAM, 0);
766         if (so < 0)
767                 err(-1, "test_socket_stream: socket");
768         close(so);
769         benchmark_start();
770         BENCHMARK_FOREACH(i, num) {
771                 so = socket(int_arg, SOCK_STREAM, 0);
772                 if (so == -1)
773                         err(-1, "test_socket_stream: socket");
774                 close(so);
775         }
776         benchmark_stop();
777         return (i);
778 }
779
780 static uintmax_t
781 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path __unused)
782 {
783         uintmax_t i;
784         int so;
785
786         so = socket(int_arg, SOCK_DGRAM, 0);
787         if (so < 0)
788                 err(-1, "test_socket_dgram: socket");
789         close(so);
790         benchmark_start();
791         BENCHMARK_FOREACH(i, num) {
792                 so = socket(int_arg, SOCK_DGRAM, 0);
793                 if (so == -1)
794                         err(-1, "test_socket_dgram: socket");
795                 close(so);
796         }
797         benchmark_stop();
798         return (i);
799 }
800
801 static uintmax_t
802 test_socketpair_stream(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
803 {
804         uintmax_t i;
805         int so[2];
806
807         if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
808                 err(-1, "test_socketpair_stream: socketpair");
809         close(so[0]);
810         close(so[1]);
811         benchmark_start();
812         BENCHMARK_FOREACH(i, num) {
813                 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
814                         err(-1, "test_socketpair_stream: socketpair");
815                 close(so[0]);
816                 close(so[1]);
817         }
818         benchmark_stop();
819         return (i);
820 }
821
822 static uintmax_t
823 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
824 {
825         uintmax_t i;
826         int so[2];
827
828         if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
829                 err(-1, "test_socketpair_dgram: socketpair");
830         close(so[0]);
831         close(so[1]);
832         benchmark_start();
833         BENCHMARK_FOREACH(i, num) {
834                 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
835                         err(-1, "test_socketpair_dgram: socketpair");
836                 close(so[0]);
837                 close(so[1]);
838         }
839         benchmark_stop();
840         return (i);
841 }
842
843 static uintmax_t
844 test_readlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
845 {
846         char buf[PATH_MAX];
847         ssize_t rv;
848         uintmax_t i;
849
850         benchmark_start();
851         BENCHMARK_FOREACH(i, num) {
852                 rv = readlink(path, buf, sizeof(buf));
853                 if (rv < 0 && errno != EINVAL)
854                         err(-1, "readlink");
855         }
856         benchmark_stop();
857         return (i);
858 }
859
860 static uintmax_t
861 test_vfork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
862 {
863         pid_t pid;
864         uintmax_t i;
865
866         pid = vfork();
867         if (pid < 0)
868                 err(-1, "test_vfork: vfork");
869         if (pid == 0)
870                 _exit(0);
871         if (waitpid(pid, NULL, 0) < 0)
872                 err(-1, "test_vfork: waitpid");
873         benchmark_start();
874         BENCHMARK_FOREACH(i, num) {
875                 pid = vfork();
876                 if (pid < 0)
877                         err(-1, "test_vfork: vfork");
878                 if (pid == 0)
879                         _exit(0);
880                 if (waitpid(pid, NULL, 0) < 0)
881                         err(-1, "test_vfork: waitpid");
882         }
883         benchmark_stop();
884         return (i);
885 }
886
887 static uintmax_t
888 test_vfork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
889 {
890         pid_t pid;
891         uintmax_t i;
892
893         pid = vfork();
894         if (pid < 0)
895                 err(-1, "test_vfork_exec: vfork");
896         if (pid == 0) {
897                 (void)execve(USR_BIN_TRUE, execve_args, environ);
898                 err(-1, "test_vfork_exec: execve");
899         }
900         if (waitpid(pid, NULL, 0) < 0)
901                 err(-1, "test_vfork_exec: waitpid");
902         benchmark_start();
903         BENCHMARK_FOREACH(i, num) {
904                 pid = vfork();
905                 if (pid < 0)
906                         err(-1, "test_vfork_exec: vfork");
907                 if (pid == 0) {
908                         (void)execve(USR_BIN_TRUE, execve_args, environ);
909                         err(-1, "execve");
910                 }
911                 if (waitpid(pid, NULL, 0) < 0)
912                         err(-1, "test_vfork_exec: waitpid");
913         }
914         benchmark_stop();
915         return (i);
916 }
917
918 struct test {
919         const char      *t_name;
920         uintmax_t       (*t_func)(uintmax_t, uintmax_t, const char *);
921         int              t_flags;
922         uintmax_t        t_int;
923 };
924
925 #define FLAG_PATH       0x00000001
926
927 static const struct test tests[] = {
928         { "access", test_access, .t_flags = FLAG_PATH },
929         { "bad_open", test_bad_open, .t_flags = 0 },
930         { "chroot", test_chroot, .t_flags = 0 },
931         { "clock_gettime", test_clock_gettime, .t_flags = 0 },
932         { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
933         { "fork", test_fork, .t_flags = 0 },
934         { "fork_exec", test_fork_exec, .t_flags = 0 },
935         { "getppid", test_getppid, .t_flags = 0 },
936         { "getpriority", test_getpriority, .t_flags = 0 },
937         { "getprogname", test_getprogname, .t_flags = 0 },
938         { "getresuid", test_getresuid, .t_flags = 0 },
939         { "gettimeofday", test_gettimeofday, .t_flags = 0 },
940         { "getuid", test_getuid, .t_flags = 0 },
941         { "lstat", test_lstat, .t_flags = FLAG_PATH },
942         { "memcpy_1", test_memcpy, .t_flags = 0, .t_int = 1 },
943         { "memcpy_10", test_memcpy, .t_flags = 0, .t_int = 10 },
944         { "memcpy_100", test_memcpy, .t_flags = 0, .t_int = 100 },
945         { "memcpy_1000", test_memcpy, .t_flags = 0, .t_int = 1000 },
946         { "memcpy_10000", test_memcpy, .t_flags = 0, .t_int = 10000 },
947         { "memcpy_100000", test_memcpy, .t_flags = 0, .t_int = 100000 },
948         { "memcpy_1000000", test_memcpy, .t_flags = 0, .t_int = 1000000 },
949         { "open_close", test_open_close, .t_flags = FLAG_PATH },
950         { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
951             .t_int = 1 },
952         { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
953             .t_int = 10 },
954         { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
955             .t_int = 100 },
956         { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
957             .t_int = 1000 },
958         { "open_read_close_10000", test_open_read_close,
959             .t_flags = FLAG_PATH, .t_int = 10000 },
960         { "open_read_close_100000", test_open_read_close,
961             .t_flags = FLAG_PATH, .t_int = 100000 },
962         { "open_read_close_1000000", test_open_read_close,
963             .t_flags = FLAG_PATH, .t_int = 1000000 },
964         { "pipe", test_pipe, .t_flags = 0 },
965         { "pipeping_1", test_pipeping, .t_flags = 0, .t_int = 1 },
966         { "pipeping_10", test_pipeping, .t_flags = 0, .t_int = 10 },
967         { "pipeping_100", test_pipeping, .t_flags = 0, .t_int = 100 },
968         { "pipeping_1000", test_pipeping, .t_flags = 0, .t_int = 1000 },
969         { "pipeping_10000", test_pipeping, .t_flags = 0, .t_int = 10000 },
970         { "pipeping_100000", test_pipeping, .t_flags = 0, .t_int = 100000 },
971         { "pipeping_1000000", test_pipeping, .t_flags = 0, .t_int = 1000000 },
972 #ifdef WITH_PTHREAD
973         { "pipepingtd_1", test_pipepingtd, .t_flags = 0, .t_int = 1 },
974         { "pipepingtd_10", test_pipepingtd, .t_flags = 0, .t_int = 10 },
975         { "pipepingtd_100", test_pipepingtd, .t_flags = 0, .t_int = 100 },
976         { "pipepingtd_1000", test_pipepingtd, .t_flags = 0, .t_int = 1000 },
977         { "pipepingtd_10000", test_pipepingtd, .t_flags = 0, .t_int = 10000 },
978         { "pipepingtd_100000", test_pipepingtd, .t_flags = 0, .t_int = 100000 },
979         { "pipepingtd_1000000", test_pipepingtd, .t_flags = 0, .t_int = 1000000 },
980 #endif
981         { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
982         { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
983         { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
984         { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
985         { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
986         { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
987         { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
988         { "select", test_select, .t_flags = 0 },
989         { "semaping", test_semaping, .t_flags = 0 },
990         { "setuid", test_setuid, .t_flags = 0 },
991         { "shmfd", test_shmfd, .t_flags = 0 },
992         { "shmfd_dup", test_shmfd_dup, .t_flags = 0 },
993         { "shmfd_fstat", test_shmfd_fstat, .t_flags = 0 },
994         { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
995         { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
996         { "socketpair_stream", test_socketpair_stream, .t_flags = 0 },
997         { "socketpair_dgram", test_socketpair_dgram, .t_flags = 0 },
998         { "socket_tcp", test_socket_stream, .t_int = PF_INET },
999         { "socket_udp", test_socket_dgram, .t_int = PF_INET },
1000         { "readlink", test_readlink, .t_flags = FLAG_PATH },
1001         { "vfork", test_vfork, .t_flags = 0 },
1002         { "vfork_exec", test_vfork_exec, .t_flags = 0 },
1003 };
1004 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
1005
1006 static void
1007 usage(void)
1008 {
1009         int i;
1010
1011         fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
1012             "[-p path] [-s seconds] test\n");
1013         for (i = 0; i < tests_count; i++)
1014                 fprintf(stderr, "  %s\n", tests[i].t_name);
1015         exit(-1);
1016 }
1017
1018 int
1019 main(int argc, char *argv[])
1020 {
1021         struct timespec ts_res;
1022         const struct test *the_test;
1023         const char *path;
1024         char *tmp_dir, *tmp_path;
1025         long long ll;
1026         char *endp;
1027         int ch, fd, error, i, j, rv;
1028         uintmax_t iterations, k, loops;
1029
1030         alarm_timeout = 1;
1031         iterations = 0;
1032         loops = 10;
1033         path = NULL;
1034         tmp_path = NULL;
1035         while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
1036                 switch (ch) {
1037                 case 'i':
1038                         ll = strtol(optarg, &endp, 10);
1039                         if (*endp != 0 || ll < 1)
1040                                 usage();
1041                         iterations = ll;
1042                         break;
1043
1044                 case 'l':
1045                         ll = strtol(optarg, &endp, 10);
1046                         if (*endp != 0 || ll < 1 || ll > 100000)
1047                                 usage();
1048                         loops = ll;
1049                         break;
1050
1051                 case 'p':
1052                         path = optarg;
1053                         break;
1054
1055                 case 's':
1056                         ll = strtol(optarg, &endp, 10);
1057                         if (*endp != 0 || ll < 1 || ll > 60*60)
1058                                 usage();
1059                         alarm_timeout = ll;
1060                         break;
1061
1062                 case '?':
1063                 default:
1064                         usage();
1065                 }
1066         }
1067         argc -= optind;
1068         argv += optind;
1069
1070         if (iterations < 1 && alarm_timeout < 1)
1071                 usage();
1072         if (iterations < 1)
1073                 iterations = UINT64_MAX;
1074         if (loops < 1)
1075                 loops = 1;
1076
1077         if (argc < 1)
1078                 usage();
1079
1080         /*
1081          * Validate test list and that, if a path is required, it is
1082          * defined.
1083          */
1084         for (j = 0; j < argc; j++) {
1085                 the_test = NULL;
1086                 for (i = 0; i < tests_count; i++) {
1087                         if (strcmp(argv[j], tests[i].t_name) == 0)
1088                                 the_test = &tests[i];
1089                 }
1090                 if (the_test == NULL)
1091                         usage();
1092                 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
1093                         tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
1094                         if (tmp_dir == NULL)
1095                                 err(1, "strdup");
1096                         tmp_dir = mkdtemp(tmp_dir);
1097                         if (tmp_dir == NULL)
1098                                 err(1, "mkdtemp");
1099                         rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
1100                         if (rv <= 0)
1101                                 err(1, "asprintf");
1102                 }
1103         }
1104
1105         error = clock_getres(CLOCK_REALTIME, &ts_res);
1106         assert(error == 0);
1107         printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
1108             (uintmax_t)ts_res.tv_nsec);
1109         printf("test\tloop\ttime\titerations\tperiteration\n");
1110
1111         for (j = 0; j < argc; j++) {
1112                 uintmax_t calls, nsecsperit;
1113
1114                 the_test = NULL;
1115                 for (i = 0; i < tests_count; i++) {
1116                         if (strcmp(argv[j], tests[i].t_name) == 0)
1117                                 the_test = &tests[i];
1118                 }
1119
1120                 if (tmp_path != NULL) {
1121                         fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
1122                         if (fd < 0)
1123                                 err(1, "cannot open %s", tmp_path);
1124                         error = ftruncate(fd, 1000000);
1125                         if (error != 0)
1126                                 err(1, "ftruncate");
1127                         error = close(fd);
1128                         if (error != 0)
1129                                 err(1, "close");
1130                         path = tmp_path;
1131                 }
1132
1133                 /*
1134                  * Run one warmup, then do the real thing (loops) times.
1135                  */
1136                 the_test->t_func(iterations, the_test->t_int, path);
1137                 calls = 0;
1138                 for (k = 0; k < loops; k++) {
1139                         calls = the_test->t_func(iterations, the_test->t_int,
1140                             path);
1141                         timespecsub(&ts_end, &ts_start, &ts_end);
1142                         printf("%s\t%ju\t", the_test->t_name, k);
1143                         printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
1144                             (uintmax_t)ts_end.tv_nsec, calls);
1145
1146                 /*
1147                  * Note.  This assumes that each iteration takes less than
1148                  * a second, and that our total nanoseconds doesn't exceed
1149                  * the room in our arithmetic unit.  Fine for system calls,
1150                  * but not for long things.
1151                  */
1152                         nsecsperit = ts_end.tv_sec * 1000000000;
1153                         nsecsperit += ts_end.tv_nsec;
1154                         nsecsperit /= calls;
1155                         printf("0.%09ju\n", (uintmax_t)nsecsperit);
1156                 }
1157         }
1158
1159         if (tmp_path != NULL) {
1160                 error = unlink(tmp_path);
1161                 if (error != 0 && errno != ENOENT)
1162                         warn("cannot unlink %s", tmp_path);
1163                 error = rmdir(tmp_dir);
1164                 if (error != 0)
1165                         warn("cannot rmdir %s", tmp_dir);
1166         }
1167
1168         return (0);
1169 }