]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/syscall_timing/syscall_timing.c
Add getpriority(2) benchmark; it's a lightweight syscall which does pretty
[FreeBSD/FreeBSD.git] / tools / tools / syscall_timing / syscall_timing.c
1 /*-
2  * Copyright (c) 2003-2004, 2010 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Portions of this software were developed at the University of Cambridge
6  * Computer Laboratory with support from a grant from Google, Inc.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31
32 #include <sys/types.h>
33 #include <sys/mman.h>
34 #include <sys/resource.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/wait.h>
39
40 #include <assert.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <inttypes.h>
45 #include <limits.h>
46 #include <signal.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 static struct timespec ts_start, ts_end;
53 static int alarm_timeout;
54 static volatile int alarm_fired;
55
56 #define timespecsub(vvp, uvp)                                           \
57         do {                                                            \
58                 (vvp)->tv_sec -= (uvp)->tv_sec;                         \
59                 (vvp)->tv_nsec -= (uvp)->tv_nsec;                       \
60                 if ((vvp)->tv_nsec < 0) {                               \
61                         (vvp)->tv_sec--;                                \
62                         (vvp)->tv_nsec += 1000000000;                   \
63                 }                                                       \
64         } while (0)
65
66 static void
67 alarm_handler(int signum)
68 {
69
70         alarm_fired = 1;
71 }
72
73 static void
74 benchmark_start(void)
75 {
76         int error;
77
78         alarm_fired = 0;
79         if (alarm_timeout) {
80                 signal(SIGALRM, alarm_handler);
81                 alarm(alarm_timeout);
82         }
83         error = clock_gettime(CLOCK_REALTIME, &ts_start);
84         assert(error == 0);
85 }
86
87 static void
88 benchmark_stop(void)
89 {
90         int error;
91
92         error = clock_gettime(CLOCK_REALTIME, &ts_end);
93         assert(error == 0);
94 }
95   
96 uintmax_t
97 test_getuid(uintmax_t num, uintmax_t int_arg, const char *path)
98 {
99         uintmax_t i;
100
101         /*
102          * Thread-local data should require no locking if system
103          * call is MPSAFE.
104          */
105         benchmark_start();
106         for (i = 0; i < num; i++) {
107                 if (alarm_fired)
108                         break;
109                 getuid();
110         }
111         benchmark_stop();
112         return (i);
113 }
114
115 uintmax_t
116 test_getppid(uintmax_t num, uintmax_t int_arg, const char *path)
117 {
118         uintmax_t i;
119
120         /*
121          * This is process-local, but can change, so will require a
122          * lock.
123          */
124         benchmark_start();
125         for (i = 0; i < num; i++) {
126                 if (alarm_fired)
127                         break;
128                 getppid();
129         }
130         benchmark_stop();
131         return (i);
132 }
133
134 uintmax_t
135 test_clock_gettime(uintmax_t num, uintmax_t int_arg, const char *path)
136 {
137         struct timespec ts;
138         uintmax_t i;
139
140         benchmark_start();
141         for (i = 0; i < num; i++) {
142                 if (alarm_fired)
143                         break;
144                 (void)clock_gettime(CLOCK_REALTIME, &ts);
145         }
146         benchmark_stop();
147         return (i);
148 }
149
150 uintmax_t
151 test_gettimeofday(uintmax_t num, uintmax_t int_arg, const char *path)
152 {
153         struct timeval tv;
154         uintmax_t i;
155
156         benchmark_start();
157         for (i = 0; i < num; i++) {
158                 if (alarm_fired)
159                         break;
160                 (void)gettimeofday(&tv, NULL);
161         }
162         benchmark_stop();
163         return (i);
164 }
165
166 uintmax_t
167 test_getpriority(uintmax_t num, uintmax_t int_arg, const char *path)
168 {
169         uintmax_t i;
170
171         benchmark_start();
172         for (i = 0; i < num; i++) {
173                 if (alarm_fired)
174                         break;
175                 (void)getpriority(PRIO_PROCESS, 0);
176         }
177         benchmark_stop();
178         return (i);
179 }
180
181
182 uintmax_t
183 test_pipe(uintmax_t num, uintmax_t int_arg, const char *path)
184 {
185         int fd[2], i;
186
187         /*
188          * pipe creation is expensive, as it will allocate a new file
189          * descriptor, allocate a new pipe, hook it all up, and return.
190          * Destroying is also expensive, as we now have to free up
191          * the file descriptors and return the pipe.
192          */
193         if (pipe(fd) < 0)
194                 err(-1, "test_pipe: pipe");
195         close(fd[0]);
196         close(fd[1]);
197         benchmark_start();
198         for (i = 0; i < num; i++) {
199                 if (alarm_fired)
200                         break;
201                 if (pipe(fd) == -1)
202                         err(-1, "test_pipe: pipe");
203                 close(fd[0]);
204                 close(fd[1]);
205         }
206         benchmark_stop();
207         return (i);
208 }
209
210 uintmax_t
211 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path)
212 {
213         uintmax_t i;
214         int so;
215
216         so = socket(int_arg, SOCK_STREAM, 0);
217         if (so < 0)
218                 err(-1, "test_socket_stream: socket");
219         close(so);
220         benchmark_start();
221         for (i = 0; i < num; i++) {
222                 if (alarm_fired)
223                         break;
224                 so = socket(int_arg, SOCK_STREAM, 0);
225                 if (so == -1)
226                         err(-1, "test_socket_stream: socket");
227                 close(so);
228         }
229         benchmark_stop();
230         return (i);
231 }
232
233 uintmax_t
234 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
235 {
236         uintmax_t i;
237         int so;
238
239         so = socket(int_arg, SOCK_DGRAM, 0);
240         if (so < 0)
241                 err(-1, "test_socket_dgram: socket");
242         close(so);
243         benchmark_start();
244         for (i = 0; i < num; i++) {
245                 if (alarm_fired)
246                         break;
247                 so = socket(int_arg, SOCK_DGRAM, 0);
248                 if (so == -1)
249                         err(-1, "test_socket_dgram: socket");
250                 close(so);
251         }
252         benchmark_stop();
253         return (i);
254 }
255
256 uintmax_t
257 test_socketpair_stream(uintmax_t num, uintmax_t int_arg, const char *path)
258 {
259         uintmax_t i;
260         int so[2];
261
262         if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
263                 err(-1, "test_socketpair_stream: socketpair");
264         close(so[0]);
265         close(so[1]);
266         benchmark_start();
267         for (i = 0; i < num; i++) {
268                 if (alarm_fired)
269                         break;
270                 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
271                         err(-1, "test_socketpair_stream: socketpair");
272                 close(so[0]);
273                 close(so[1]);
274         }
275         benchmark_stop();
276         return (i);
277 }
278
279 uintmax_t
280 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
281 {
282         uintmax_t i;
283         int so[2];
284
285         if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
286                 err(-1, "test_socketpair_dgram: socketpair");
287         close(so[0]);
288         close(so[1]);
289         benchmark_start();
290         for (i = 0; i < num; i++) {
291                 if (alarm_fired)
292                         break;
293                 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
294                         err(-1, "test_socketpair_dgram: socketpair");
295                 close(so[0]);
296                 close(so[1]);
297         }
298         benchmark_stop();
299         return (i);
300 }
301
302 uintmax_t
303 test_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path)
304 {
305         uintmax_t i;
306         int fd;
307
308         (void)unlink(path);
309         fd = open(path, O_RDWR | O_CREAT, 0600);
310         if (fd < 0)
311                 err(-1, "test_create_unlink: create: %s", path);
312         close(fd);
313         if (unlink(path) < 0)
314                 err(-1, "test_create_unlink: unlink: %s", path);
315         benchmark_start();
316         for (i = 0; i < num; i++) {
317                 if (alarm_fired)
318                         break;
319                 fd = open(path, O_RDWR | O_CREAT, 0600);
320                 if (fd < 0)
321                         err(-1, "test_create_unlink: create: %s", path);
322                 close(fd);
323                 if (unlink(path) < 0)
324                         err(-1, "test_create_unlink: unlink: %s", path);
325         }
326         benchmark_stop();
327         return (i);
328 }
329
330 uintmax_t
331 test_open_close(uintmax_t num, uintmax_t int_arg, const char *path)
332 {
333         uintmax_t i;
334         int fd;
335
336         fd = open(path, O_RDONLY);
337         if (fd < 0)
338                 err(-1, "test_open_close: %s", path);
339         close(fd);
340
341         benchmark_start();
342         for (i = 0; i < num; i++) {
343                 if (alarm_fired)
344                         break;
345                 fd = open(path, O_RDONLY);
346                 if (fd < 0)
347                         err(-1, "test_open_close: %s", path);
348                 close(fd);
349         }
350         benchmark_stop();
351         return (i);
352 }
353
354 uintmax_t
355 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
356 {
357         char buf[int_arg];
358         uintmax_t i;
359         int fd;
360
361         fd = open(path, O_RDONLY);
362         if (fd < 0)
363                 err(-1, "test_open_read: %s", path);
364         (void)pread(fd, buf, int_arg, 0);
365
366         benchmark_start();
367         for (i = 0; i < num; i++) {
368                 if (alarm_fired)
369                         break;
370                 (void)pread(fd, buf, int_arg, 0);
371         }
372         benchmark_stop();
373         close(fd);
374         return (i);
375 }
376
377 uintmax_t
378 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
379 {
380         char buf[int_arg];
381         uintmax_t i;
382         int fd;
383
384         fd = open(path, O_RDONLY);
385         if (fd < 0)
386                 err(-1, "test_open_read_close: %s", path);
387         (void)read(fd, buf, int_arg);
388         close(fd);
389
390         benchmark_start();
391         for (i = 0; i < num; i++) {
392                 if (alarm_fired)
393                         break;
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 uintmax_t
405 test_dup(uintmax_t num, uintmax_t int_arg, const char *path)
406 {
407         int fd, i, shmfd;
408
409         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
410         if (shmfd < 0)
411                 err(-1, "test_dup: shm_open");
412         fd = dup(shmfd);
413         if (fd >= 0)
414                 close(fd);
415         benchmark_start();
416         for (i = 0; i < num; i++) {
417                 if (alarm_fired)
418                         break;
419                 fd = dup(shmfd);
420                 if (fd >= 0)
421                         close(fd);
422         }
423         benchmark_stop();
424         close(shmfd);
425         return (i);
426 }
427
428 uintmax_t
429 test_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
430 {
431         uintmax_t i;
432         int shmfd;
433
434         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
435         if (shmfd < 0)
436                 err(-1, "test_shmfd: shm_open");
437         close(shmfd);
438         benchmark_start();
439         for (i = 0; i < num; i++) {
440                 if (alarm_fired)
441                         break;
442                 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
443                 if (shmfd < 0)
444                         err(-1, "test_shmfd: shm_open");
445                 close(shmfd);
446         }
447         benchmark_stop();
448         return (i);
449 }
450
451 uintmax_t
452 test_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
453 {
454         struct stat sb;
455         uintmax_t i;
456         int shmfd;
457
458         shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
459         if (shmfd < 0)
460                 err(-1, "test_fstat_shmfd: shm_open");
461         if (fstat(shmfd, &sb) < 0)
462                 err(-1, "test_fstat_shmfd: fstat");
463         benchmark_start();
464         for (i = 0; i < num; i++) {
465                 if (alarm_fired)
466                         break;
467                 (void)fstat(shmfd, &sb);
468         }
469         benchmark_stop();
470         close(shmfd);
471         return (i);
472 }
473
474 uintmax_t
475 test_fork(uintmax_t num, uintmax_t int_arg, const char *path)
476 {
477         pid_t pid;
478         uintmax_t i;
479
480         pid = fork();
481         if (pid < 0)
482                 err(-1, "test_fork: fork");
483         if (pid == 0)
484                 _exit(0);
485         if (waitpid(pid, NULL, 0) < 0)
486                 err(-1, "test_fork: waitpid");
487         benchmark_start();
488         for (i = 0; i < num; i++) {
489                 if (alarm_fired)
490                         break;
491                 pid = fork();
492                 if (pid < 0)
493                         err(-1, "test_fork: fork");
494                 if (pid == 0)
495                         _exit(0);
496                 if (waitpid(pid, NULL, 0) < 0)
497                         err(-1, "test_fork: waitpid");
498         }
499         benchmark_stop();
500         return (i);
501 }
502
503 uintmax_t
504 test_vfork(uintmax_t num, uintmax_t int_arg, const char *path)
505 {
506         pid_t pid;
507         uintmax_t i;
508
509         pid = vfork();
510         if (pid < 0)
511                 err(-1, "test_vfork: vfork");
512         if (pid == 0)
513                 _exit(0);
514         if (waitpid(pid, NULL, 0) < 0)
515                 err(-1, "test_vfork: waitpid");
516         benchmark_start();
517         for (i = 0; i < num; i++) {
518                 if (alarm_fired)
519                         break;
520                 pid = vfork();
521                 if (pid < 0)
522                         err(-1, "test_vfork: vfork");
523                 if (pid == 0)
524                         _exit(0);
525                 if (waitpid(pid, NULL, 0) < 0)
526                         err(-1, "test_vfork: waitpid");
527         }
528         benchmark_stop();
529         return (i);
530 }
531
532 #define USR_BIN_TRUE    "/usr/bin/true"
533 static char *execve_args[] = { USR_BIN_TRUE, NULL};
534 extern char **environ;
535
536 uintmax_t
537 test_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
538 {
539         pid_t pid;
540         uintmax_t i;
541
542         pid = fork();
543         if (pid < 0)
544                 err(-1, "test_fork_exec: fork");
545         if (pid == 0) {
546                 (void)execve(USR_BIN_TRUE, execve_args, environ);
547                 err(-1, "execve");
548         }
549         if (waitpid(pid, NULL, 0) < 0)
550                 err(-1, "test_fork: waitpid");
551         benchmark_start();
552         for (i = 0; i < num; i++) {
553                 if (alarm_fired)
554                         break;
555                 pid = fork();
556                 if (pid < 0)
557                         err(-1, "test_fork_exec: fork");
558                 if (pid == 0) {
559                         (void)execve(USR_BIN_TRUE, execve_args, environ);
560                         err(-1, "test_fork_exec: execve");
561                 }
562                 if (waitpid(pid, NULL, 0) < 0)
563                         err(-1, "test_fork_exec: waitpid");
564         }
565         benchmark_stop();
566         return (i);
567 }
568
569 uintmax_t
570 test_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
571 {
572         pid_t pid;
573         uintmax_t i;
574
575         pid = vfork();
576         if (pid < 0)
577                 err(-1, "test_vfork_exec: vfork");
578         if (pid == 0) {
579                 (void)execve(USR_BIN_TRUE, execve_args, environ);
580                 err(-1, "test_vfork_exec: execve");
581         }
582         if (waitpid(pid, NULL, 0) < 0)
583                 err(-1, "test_vfork_exec: waitpid");
584         benchmark_start();
585         for (i = 0; i < num; i++) {
586                 if (alarm_fired)
587                         break;
588                 pid = vfork();
589                 if (pid < 0)
590                         err(-1, "test_vfork_exec: vfork");
591                 if (pid == 0) {
592                         (void)execve(USR_BIN_TRUE, execve_args, environ);
593                         err(-1, "execve");
594                 }
595                 if (waitpid(pid, NULL, 0) < 0)
596                         err(-1, "test_vfork_exec: waitpid");
597         }
598         benchmark_stop();
599         return (i);
600 }
601
602 uintmax_t
603 test_chroot(uintmax_t num, uintmax_t int_arg, const char *path)
604 {
605         uintmax_t i;
606
607         if (chroot("/") < 0)
608                 err(-1, "test_chroot: chroot");
609         benchmark_start();
610         for (i = 0; i < num; i++) {
611                 if (alarm_fired)
612                         break;
613                 if (chroot("/") < 0)
614                         err(-1, "test_chroot: chroot");
615         }
616         benchmark_stop();
617         return (i);
618 }
619
620 uintmax_t
621 test_setuid(uintmax_t num, uintmax_t int_arg, const char *path)
622 {
623         uid_t uid;
624         uintmax_t i;
625
626         uid = getuid();
627         if (setuid(uid) < 0)
628                 err(-1, "test_setuid: setuid");
629         benchmark_start();
630         for (i = 0; i < num; i++) {
631                 if (alarm_fired)
632                         break;
633                 if (setuid(uid) < 0)
634                         err(-1, "test_setuid: setuid");
635         }
636         benchmark_stop();
637         return (i);
638 }
639
640 struct test {
641         const char      *t_name;
642         uintmax_t       (*t_func)(uintmax_t, uintmax_t, const char *);
643         int              t_flags;
644         uintmax_t        t_int;
645 };
646
647 #define FLAG_PATH       0x00000001
648
649 static const struct test tests[] = {
650         { "getuid", test_getuid },
651         { "getppid", test_getppid },
652         { "clock_gettime", test_clock_gettime },
653         { "gettimeofday", test_gettimeofday },
654         { "getpriority", test_getpriority },
655         { "pipe", test_pipe },
656         { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
657         { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
658         { "socketpair_stream", test_socketpair_stream },
659         { "socketpair_dgram", test_socketpair_dgram },
660         { "socket_tcp", test_socket_stream, .t_int = PF_INET },
661         { "socket_udp", test_socket_dgram, .t_int = PF_INET },
662         { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
663         { "open_close", test_open_close, .t_flags = FLAG_PATH },
664         { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
665             .t_int = 1 },
666         { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
667             .t_int = 10 },
668         { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
669             .t_int = 100 },
670         { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
671             .t_int = 1000 },
672         { "open_read_close_10000", test_open_read_close,
673             .t_flags = FLAG_PATH, .t_int = 10000 },
674         { "open_read_close_100000", test_open_read_close,
675             .t_flags = FLAG_PATH, .t_int = 100000 },
676         { "open_read_close_1000000", test_open_read_close,
677             .t_flags = FLAG_PATH, .t_int = 1000000 },
678         { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
679         { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
680         { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
681         { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
682         { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
683         { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
684         { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
685         { "dup", test_dup },
686         { "shmfd", test_shmfd },
687         { "fstat_shmfd", test_fstat_shmfd },
688         { "fork", test_fork },
689         { "vfork", test_vfork },
690         { "fork_exec", test_fork_exec },
691         { "vfork_exec", test_vfork_exec },
692         { "chroot", test_chroot },
693         { "setuid", test_setuid },
694 };
695 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
696
697 static void
698 usage(void)
699 {
700         int i;
701
702         fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
703             "[-p path] [-s seconds] test\n");
704         for (i = 0; i < tests_count; i++)
705                 fprintf(stderr, "  %s\n", tests[i].t_name);
706         exit(-1);
707 }
708
709 int
710 main(int argc, char *argv[])
711 {
712         struct timespec ts_res;
713         const struct test *the_test;
714         const char *path;
715         char *tmp_dir, *tmp_path;
716         long long ll;
717         char *endp;
718         int ch, fd, error, i, j, k, rv;
719         uintmax_t iterations, loops;
720
721         alarm_timeout = 1;
722         iterations = 0;
723         loops = 10;
724         path = NULL;
725         tmp_path = NULL;
726         while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
727                 switch (ch) {
728                 case 'i':
729                         ll = strtol(optarg, &endp, 10);
730                         if (*endp != 0 || ll < 1 || ll > 100000)
731                                 usage();
732                         iterations = ll;
733                         break;
734
735                 case 'l':
736                         ll = strtol(optarg, &endp, 10);
737                         if (*endp != 0 || ll < 1 || ll > 100000)
738                                 usage();
739                         loops = ll;
740                         break;
741
742                 case 'p':
743                         path = optarg;
744                         break;
745
746                 case 's':
747                         ll = strtol(optarg, &endp, 10);
748                         if (*endp != 0 || ll < 1 || ll > 60*60)
749                                 usage();
750                         alarm_timeout = ll;
751                         break;
752
753                 case '?':
754                 default:
755                         usage();
756                 }
757         }
758         argc -= optind;
759         argv += optind;
760
761         if (iterations < 1 && alarm_timeout < 1)
762                 usage();
763         if (iterations < 1)
764                 iterations = UINT64_MAX;
765         if (loops < 1)
766                 loops = 1;
767
768         if (argc < 1)
769                 usage();
770
771         /*
772          * Validate test list and that, if a path is required, it is
773          * defined.
774          */
775         for (j = 0; j < argc; j++) {
776                 the_test = NULL;
777                 for (i = 0; i < tests_count; i++) {
778                         if (strcmp(argv[j], tests[i].t_name) == 0)
779                                 the_test = &tests[i];
780                 }
781                 if (the_test == NULL)
782                         usage();
783                 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
784                         tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
785                         if (tmp_dir == NULL)
786                                 err(1, "strdup");
787                         tmp_dir = mkdtemp(tmp_dir);
788                         if (tmp_dir == NULL)
789                                 err(1, "mkdtemp");
790                         rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
791                         if (rv <= 0)
792                                 err(1, "asprintf");
793                 }
794         }
795
796         error = clock_getres(CLOCK_REALTIME, &ts_res);
797         assert(error == 0);
798         printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
799             (uintmax_t)ts_res.tv_nsec);
800         printf("test\tloop\ttime\titerations\tperiteration\n");
801
802         for (j = 0; j < argc; j++) {
803                 uintmax_t calls, nsecsperit;
804
805                 the_test = NULL;
806                 for (i = 0; i < tests_count; i++) {
807                         if (strcmp(argv[j], tests[i].t_name) == 0)
808                                 the_test = &tests[i];
809                 }
810
811                 if (tmp_path != NULL) {
812                         fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
813                         if (fd < 0)
814                                 err(1, "cannot open %s", tmp_path);
815                         error = ftruncate(fd, 1000000);
816                         if (error != 0)
817                                 err(1, "ftruncate");
818                         error = close(fd);
819                         if (error != 0)
820                                 err(1, "close");
821                         path = tmp_path;
822                 }
823
824                 /*
825                  * Run one warmup, then do the real thing (loops) times.
826                  */
827                 the_test->t_func(iterations, the_test->t_int, path);
828                 calls = 0;
829                 for (k = 0; k < loops; k++) {
830                         calls = the_test->t_func(iterations, the_test->t_int,
831                             path);
832                         timespecsub(&ts_end, &ts_start);
833                         printf("%s\t%d\t", the_test->t_name, k);
834                         printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
835                             (uintmax_t)ts_end.tv_nsec, calls);
836
837                 /*
838                  * Note.  This assumes that each iteration takes less than
839                  * a second, and that our total nanoseconds doesn't exceed
840                  * the room in our arithmetic unit.  Fine for system calls,
841                  * but not for long things.
842                  */
843                         nsecsperit = ts_end.tv_sec * 1000000000;
844                         nsecsperit += ts_end.tv_nsec;
845                         nsecsperit /= calls;
846                         printf("0.%09ju\n", (uintmax_t)nsecsperit);
847                 }
848         }
849
850         if (tmp_path != NULL) {
851                 error = unlink(tmp_path);
852                 if (error != 0 && errno != ENOENT)
853                         warn("cannot unlink %s", tmp_path);
854                 error = rmdir(tmp_dir);
855                 if (error != 0)
856                         warn("cannot rmdir %s", tmp_dir);
857         }
858
859         return (0);
860 }