2 * Copyright (c) 2003-2004, 2010 Robert N. M. Watson
5 * Portions of this software were developed at the University of Cambridge
6 * Computer Laboratory with support from a grant from Google, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
32 #include <sys/types.h>
34 #include <sys/socket.h>
50 static struct timespec ts_start, ts_end;
51 static int alarm_timeout;
52 static volatile int alarm_fired;
54 #define timespecsub(vvp, uvp) \
56 (vvp)->tv_sec -= (uvp)->tv_sec; \
57 (vvp)->tv_nsec -= (uvp)->tv_nsec; \
58 if ((vvp)->tv_nsec < 0) { \
60 (vvp)->tv_nsec += 1000000000; \
65 alarm_handler(int signum)
78 signal(SIGALRM, alarm_handler);
81 error = clock_gettime(CLOCK_REALTIME, &ts_start);
90 error = clock_gettime(CLOCK_REALTIME, &ts_end);
95 test_getuid(uintmax_t num, uintmax_t int_arg, const char *path)
100 * Thread-local data should require no locking if system
104 for (i = 0; i < num; i++) {
114 test_getppid(uintmax_t num, uintmax_t int_arg, const char *path)
119 * This is process-local, but can change, so will require a
123 for (i = 0; i < num; i++) {
133 test_clock_gettime(uintmax_t num, uintmax_t int_arg, const char *path)
139 for (i = 0; i < num; i++) {
142 (void)clock_gettime(CLOCK_REALTIME, &ts);
149 test_gettimeofday(uintmax_t num, uintmax_t int_arg, const char *path)
155 for (i = 0; i < num; i++) {
158 (void)gettimeofday(&tv, NULL);
165 test_pipe(uintmax_t num, uintmax_t int_arg, const char *path)
170 * pipe creation is expensive, as it will allocate a new file
171 * descriptor, allocate a new pipe, hook it all up, and return.
172 * Destroying is also expensive, as we now have to free up
173 * the file descriptors and return the pipe.
176 err(-1, "test_pipe: pipe");
180 for (i = 0; i < num; i++) {
184 err(-1, "test_pipe: pipe");
193 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path)
197 so = socket(int_arg, SOCK_STREAM, 0);
199 err(-1, "test_socket_stream: socket");
202 for (i = 0; i < num; i++) {
205 so = socket(int_arg, SOCK_STREAM, 0);
207 err(-1, "test_socket_stream: socket");
215 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
219 so = socket(int_arg, SOCK_DGRAM, 0);
221 err(-1, "test_socket_dgram: socket");
224 for (i = 0; i < num; i++) {
227 so = socket(int_arg, SOCK_DGRAM, 0);
229 err(-1, "test_socket_dgram: socket");
237 test_socketpair_stream(uintmax_t num, uintmax_t int_arg, const char *path)
242 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
243 err(-1, "test_socketpair_stream: socketpair");
247 for (i = 0; i < num; i++) {
250 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
251 err(-1, "test_socketpair_stream: socketpair");
260 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
265 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
266 err(-1, "test_socketpair_dgram: socketpair");
270 for (i = 0; i < num; i++) {
273 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
274 err(-1, "test_socketpair_dgram: socketpair");
283 test_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path)
289 fd = open(path, O_RDWR | O_CREAT, 0600);
291 err(-1, "test_create_unlink: create: %s", path);
293 if (unlink(path) < 0)
294 err(-1, "test_create_unlink: unlink: %s", path);
296 for (i = 0; i < num; i++) {
299 fd = open(path, O_RDWR | O_CREAT, 0600);
301 err(-1, "test_create_unlink: create: %s", path);
303 if (unlink(path) < 0)
304 err(-1, "test_create_unlink: unlink: %s", path);
311 test_open_close(uintmax_t num, uintmax_t int_arg, const char *path)
316 fd = open(path, O_RDONLY);
318 err(-1, "test_open_close: %s", path);
322 for (i = 0; i < num; i++) {
325 fd = open(path, O_RDONLY);
327 err(-1, "test_open_close: %s", path);
335 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
341 fd = open(path, O_RDONLY);
343 err(-1, "test_open_read: %s", path);
344 (void)pread(fd, buf, int_arg, 0);
347 for (i = 0; i < num; i++) {
350 (void)pread(fd, buf, int_arg, 0);
358 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
364 fd = open(path, O_RDONLY);
366 err(-1, "test_open_read_close: %s", path);
367 (void)read(fd, buf, int_arg);
371 for (i = 0; i < num; i++) {
374 fd = open(path, O_RDONLY);
376 err(-1, "test_open_read_close: %s", path);
377 (void)read(fd, buf, int_arg);
385 test_dup(uintmax_t num, uintmax_t int_arg, const char *path)
389 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
391 err(-1, "test_dup: shm_open");
396 for (i = 0; i < num; i++) {
409 test_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
413 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
415 err(-1, "test_shmfd: shm_open");
418 for (i = 0; i < num; i++) {
421 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
423 err(-1, "test_shmfd: shm_open");
431 test_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
436 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
438 err(-1, "test_fstat_shmfd: shm_open");
439 if (fstat(shmfd, &sb) < 0)
440 err(-1, "test_fstat_shmfd: fstat");
442 for (i = 0; i < num; i++) {
445 (void)fstat(shmfd, &sb);
453 test_fork(uintmax_t num, uintmax_t int_arg, const char *path)
460 err(-1, "test_fork: fork");
463 if (waitpid(pid, NULL, 0) < 0)
464 err(-1, "test_fork: waitpid");
466 for (i = 0; i < num; i++) {
471 err(-1, "test_fork: fork");
474 if (waitpid(pid, NULL, 0) < 0)
475 err(-1, "test_fork: waitpid");
482 test_vfork(uintmax_t num, uintmax_t int_arg, const char *path)
489 err(-1, "test_vfork: vfork");
492 if (waitpid(pid, NULL, 0) < 0)
493 err(-1, "test_vfork: waitpid");
495 for (i = 0; i < num; i++) {
500 err(-1, "test_vfork: vfork");
503 if (waitpid(pid, NULL, 0) < 0)
504 err(-1, "test_vfork: waitpid");
510 #define USR_BIN_TRUE "/usr/bin/true"
511 static char *execve_args[] = { USR_BIN_TRUE, NULL};
512 extern char **environ;
515 test_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
522 err(-1, "test_fork_exec: fork");
524 (void)execve(USR_BIN_TRUE, execve_args, environ);
527 if (waitpid(pid, NULL, 0) < 0)
528 err(-1, "test_fork: waitpid");
530 for (i = 0; i < num; i++) {
535 err(-1, "test_fork_exec: fork");
537 (void)execve(USR_BIN_TRUE, execve_args, environ);
538 err(-1, "test_fork_exec: execve");
540 if (waitpid(pid, NULL, 0) < 0)
541 err(-1, "test_fork_exec: waitpid");
548 test_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
555 err(-1, "test_vfork_exec: vfork");
557 (void)execve(USR_BIN_TRUE, execve_args, environ);
558 err(-1, "test_vfork_exec: execve");
560 if (waitpid(pid, NULL, 0) < 0)
561 err(-1, "test_vfork_exec: waitpid");
563 for (i = 0; i < num; i++) {
568 err(-1, "test_vfork_exec: vfork");
570 (void)execve(USR_BIN_TRUE, execve_args, environ);
573 if (waitpid(pid, NULL, 0) < 0)
574 err(-1, "test_vfork_exec: waitpid");
581 test_chroot(uintmax_t num, uintmax_t int_arg, const char *path)
586 err(-1, "test_chroot: chroot");
588 for (i = 0; i < num; i++) {
592 err(-1, "test_chroot: chroot");
599 test_setuid(uintmax_t num, uintmax_t int_arg, const char *path)
606 err(-1, "test_setuid: setuid");
608 for (i = 0; i < num; i++) {
612 err(-1, "test_setuid: setuid");
620 uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *);
625 #define FLAG_PATH 0x00000001
627 static const struct test tests[] = {
628 { "getuid", test_getuid },
629 { "getppid", test_getppid },
630 { "clock_gettime", test_clock_gettime },
631 { "gettimeofday", test_gettimeofday },
632 { "pipe", test_pipe },
633 { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
634 { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
635 { "socketpair_stream", test_socketpair_stream },
636 { "socketpair_dgram", test_socketpair_dgram },
637 { "socket_tcp", test_socket_stream, .t_int = PF_INET },
638 { "socket_udp", test_socket_dgram, .t_int = PF_INET },
639 { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
640 { "open_close", test_open_close, .t_flags = FLAG_PATH },
641 { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
643 { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
645 { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
647 { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
649 { "open_read_close_10000", test_open_read_close,
650 .t_flags = FLAG_PATH, .t_int = 10000 },
651 { "open_read_close_100000", test_open_read_close,
652 .t_flags = FLAG_PATH, .t_int = 100000 },
653 { "open_read_close_1000000", test_open_read_close,
654 .t_flags = FLAG_PATH, .t_int = 1000000 },
655 { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
656 { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
657 { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
658 { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
659 { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
660 { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
661 { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
663 { "shmfd", test_shmfd },
664 { "fstat_shmfd", test_fstat_shmfd },
665 { "fork", test_fork },
666 { "vfork", test_vfork },
667 { "fork_exec", test_fork_exec },
668 { "vfork_exec", test_vfork_exec },
669 { "chroot", test_chroot },
670 { "setuid", test_setuid },
672 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
679 fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
680 "[-p path] [-s seconds] test\n");
681 for (i = 0; i < tests_count; i++)
682 fprintf(stderr, " %s\n", tests[i].t_name);
687 main(int argc, char *argv[])
689 struct timespec ts_res;
690 const struct test *the_test;
694 int ch, error, i, j, k;
695 uintmax_t iterations, loops;
701 while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
704 ll = strtol(optarg, &endp, 10);
705 if (*endp != 0 || ll < 1 || ll > 100000)
711 ll = strtol(optarg, &endp, 10);
712 if (*endp != 0 || ll < 1 || ll > 100000)
722 ll = strtol(optarg, &endp, 10);
723 if (*endp != 0 || ll < 1 || ll > 60*60)
736 if (iterations < 1 && alarm_timeout < 1)
739 iterations = UINT64_MAX;
747 * Validate test list and that, if a path is required, it is
750 for (j = 0; j < argc; j++) {
752 for (i = 0; i < tests_count; i++) {
753 if (strcmp(argv[j], tests[i].t_name) == 0)
754 the_test = &tests[i];
756 if (the_test == NULL)
758 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
759 errx(-1, "%s requires -p", the_test->t_name);
763 error = clock_getres(CLOCK_REALTIME, &ts_res);
765 printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
766 (uintmax_t)ts_res.tv_nsec);
767 printf("test\tloop\ttime\titerations\tperiteration\n");
769 for (j = 0; j < argc; j++) {
770 uintmax_t calls, nsecsperit;
773 for (i = 0; i < tests_count; i++) {
774 if (strcmp(argv[j], tests[i].t_name) == 0)
775 the_test = &tests[i];
779 * Run one warmup, then do the real thing (loops) times.
781 the_test->t_func(iterations, the_test->t_int, path);
783 for (k = 0; k < loops; k++) {
784 calls = the_test->t_func(iterations, the_test->t_int,
786 timespecsub(&ts_end, &ts_start);
787 printf("%s\t%d\t", the_test->t_name, k);
788 printf("%ju.%09ju\t%d\t", (uintmax_t)ts_end.tv_sec,
789 (uintmax_t)ts_end.tv_nsec, calls);
792 * Note. This assumes that each iteration takes less than
793 * a second, and that our total nanoseconds doesn't exceed
794 * the room in our arithmetic unit. Fine for system calls,
795 * but not for long things.
797 nsecsperit = ts_end.tv_sec * 1000000000;
798 nsecsperit += ts_end.tv_nsec;
800 printf("0.%09ju\n", (uintmax_t)nsecsperit);