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/resource.h>
35 #include <sys/socket.h>
52 static struct timespec ts_start, ts_end;
53 static int alarm_timeout;
54 static volatile int alarm_fired;
56 #define timespecsub(vvp, uvp) \
58 (vvp)->tv_sec -= (uvp)->tv_sec; \
59 (vvp)->tv_nsec -= (uvp)->tv_nsec; \
60 if ((vvp)->tv_nsec < 0) { \
62 (vvp)->tv_nsec += 1000000000; \
67 alarm_handler(int signum)
80 signal(SIGALRM, alarm_handler);
83 error = clock_gettime(CLOCK_REALTIME, &ts_start);
92 error = clock_gettime(CLOCK_REALTIME, &ts_end);
97 test_getuid(uintmax_t num, uintmax_t int_arg, const char *path)
102 * Thread-local data should require no locking if system
106 for (i = 0; i < num; i++) {
116 test_getppid(uintmax_t num, uintmax_t int_arg, const char *path)
121 * This is process-local, but can change, so will require a
125 for (i = 0; i < num; i++) {
135 test_getresuid(uintmax_t num, uintmax_t int_arg, const char *path)
137 uid_t ruid, euid, suid;
141 for (i = 0; i < num; i++) {
144 (void)getresuid(&ruid, &euid, &suid);
151 test_clock_gettime(uintmax_t num, uintmax_t int_arg, const char *path)
157 for (i = 0; i < num; i++) {
160 (void)clock_gettime(CLOCK_REALTIME, &ts);
167 test_gettimeofday(uintmax_t num, uintmax_t int_arg, const char *path)
173 for (i = 0; i < num; i++) {
176 (void)gettimeofday(&tv, NULL);
183 test_getpriority(uintmax_t num, uintmax_t int_arg, const char *path)
188 for (i = 0; i < num; i++) {
191 (void)getpriority(PRIO_PROCESS, 0);
198 test_pipe(uintmax_t num, uintmax_t int_arg, const char *path)
203 * pipe creation is expensive, as it will allocate a new file
204 * descriptor, allocate a new pipe, hook it all up, and return.
205 * Destroying is also expensive, as we now have to free up
206 * the file descriptors and return the pipe.
209 err(-1, "test_pipe: pipe");
213 for (i = 0; i < num; i++) {
217 err(-1, "test_pipe: pipe");
226 test_select(uintmax_t num, uintmax_t int_arg, const char *path)
228 fd_set readfds, writefds, exceptfds;
241 for (i = 0; i < num; i++) {
244 (void)select(0, &readfds, &writefds, &exceptfds, &tv);
251 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path)
256 so = socket(int_arg, SOCK_STREAM, 0);
258 err(-1, "test_socket_stream: socket");
261 for (i = 0; i < num; i++) {
264 so = socket(int_arg, SOCK_STREAM, 0);
266 err(-1, "test_socket_stream: socket");
274 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
279 so = socket(int_arg, SOCK_DGRAM, 0);
281 err(-1, "test_socket_dgram: socket");
284 for (i = 0; i < num; i++) {
287 so = socket(int_arg, SOCK_DGRAM, 0);
289 err(-1, "test_socket_dgram: socket");
297 test_socketpair_stream(uintmax_t num, uintmax_t int_arg, const char *path)
302 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
303 err(-1, "test_socketpair_stream: socketpair");
307 for (i = 0; i < num; i++) {
310 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
311 err(-1, "test_socketpair_stream: socketpair");
320 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
325 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
326 err(-1, "test_socketpair_dgram: socketpair");
330 for (i = 0; i < num; i++) {
333 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
334 err(-1, "test_socketpair_dgram: socketpair");
343 test_access(uintmax_t num, uintmax_t int_arg, const char *path)
348 fd = access(path, O_RDONLY);
350 err(-1, "test_access: %s", path);
354 for (i = 0; i < num; i++) {
357 access(path, O_RDONLY);
365 test_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path)
371 fd = open(path, O_RDWR | O_CREAT, 0600);
373 err(-1, "test_create_unlink: create: %s", path);
375 if (unlink(path) < 0)
376 err(-1, "test_create_unlink: unlink: %s", path);
378 for (i = 0; i < num; i++) {
381 fd = open(path, O_RDWR | O_CREAT, 0600);
383 err(-1, "test_create_unlink: create: %s", path);
385 if (unlink(path) < 0)
386 err(-1, "test_create_unlink: unlink: %s", path);
393 test_open_close(uintmax_t num, uintmax_t int_arg, const char *path)
398 fd = open(path, O_RDONLY);
400 err(-1, "test_open_close: %s", path);
404 for (i = 0; i < num; i++) {
407 fd = open(path, O_RDONLY);
409 err(-1, "test_open_close: %s", path);
417 test_bad_open(uintmax_t num, uintmax_t int_arg, const char *path)
422 for (i = 0; i < num; i++) {
432 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
438 fd = open(path, O_RDONLY);
440 err(-1, "test_open_read: %s", path);
441 (void)pread(fd, buf, int_arg, 0);
444 for (i = 0; i < num; i++) {
447 (void)pread(fd, buf, int_arg, 0);
455 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
461 fd = open(path, O_RDONLY);
463 err(-1, "test_open_read_close: %s", path);
464 (void)read(fd, buf, int_arg);
468 for (i = 0; i < num; i++) {
471 fd = open(path, O_RDONLY);
473 err(-1, "test_open_read_close: %s", path);
474 (void)read(fd, buf, int_arg);
482 test_dup(uintmax_t num, uintmax_t int_arg, const char *path)
486 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
488 err(-1, "test_dup: shm_open");
493 for (i = 0; i < num; i++) {
506 test_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
511 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
513 err(-1, "test_shmfd: shm_open");
516 for (i = 0; i < num; i++) {
519 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
521 err(-1, "test_shmfd: shm_open");
529 test_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
535 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
537 err(-1, "test_fstat_shmfd: shm_open");
538 if (fstat(shmfd, &sb) < 0)
539 err(-1, "test_fstat_shmfd: fstat");
541 for (i = 0; i < num; i++) {
544 (void)fstat(shmfd, &sb);
552 test_fork(uintmax_t num, uintmax_t int_arg, const char *path)
559 err(-1, "test_fork: fork");
562 if (waitpid(pid, NULL, 0) < 0)
563 err(-1, "test_fork: waitpid");
565 for (i = 0; i < num; i++) {
570 err(-1, "test_fork: fork");
573 if (waitpid(pid, NULL, 0) < 0)
574 err(-1, "test_fork: waitpid");
581 test_vfork(uintmax_t num, uintmax_t int_arg, const char *path)
588 err(-1, "test_vfork: vfork");
591 if (waitpid(pid, NULL, 0) < 0)
592 err(-1, "test_vfork: waitpid");
594 for (i = 0; i < num; i++) {
599 err(-1, "test_vfork: vfork");
602 if (waitpid(pid, NULL, 0) < 0)
603 err(-1, "test_vfork: waitpid");
609 #define USR_BIN_TRUE "/usr/bin/true"
610 static char *execve_args[] = { USR_BIN_TRUE, NULL};
611 extern char **environ;
614 test_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
621 err(-1, "test_fork_exec: fork");
623 (void)execve(USR_BIN_TRUE, execve_args, environ);
626 if (waitpid(pid, NULL, 0) < 0)
627 err(-1, "test_fork: waitpid");
629 for (i = 0; i < num; i++) {
634 err(-1, "test_fork_exec: fork");
636 (void)execve(USR_BIN_TRUE, execve_args, environ);
637 err(-1, "test_fork_exec: execve");
639 if (waitpid(pid, NULL, 0) < 0)
640 err(-1, "test_fork_exec: waitpid");
647 test_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
654 err(-1, "test_vfork_exec: vfork");
656 (void)execve(USR_BIN_TRUE, execve_args, environ);
657 err(-1, "test_vfork_exec: execve");
659 if (waitpid(pid, NULL, 0) < 0)
660 err(-1, "test_vfork_exec: waitpid");
662 for (i = 0; i < num; i++) {
667 err(-1, "test_vfork_exec: vfork");
669 (void)execve(USR_BIN_TRUE, execve_args, environ);
672 if (waitpid(pid, NULL, 0) < 0)
673 err(-1, "test_vfork_exec: waitpid");
680 test_chroot(uintmax_t num, uintmax_t int_arg, const char *path)
685 err(-1, "test_chroot: chroot");
687 for (i = 0; i < num; i++) {
691 err(-1, "test_chroot: chroot");
698 test_setuid(uintmax_t num, uintmax_t int_arg, const char *path)
705 err(-1, "test_setuid: setuid");
707 for (i = 0; i < num; i++) {
711 err(-1, "test_setuid: setuid");
719 uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *);
724 #define FLAG_PATH 0x00000001
726 static const struct test tests[] = {
727 { "getuid", test_getuid },
728 { "getppid", test_getppid },
729 { "getresuid", test_getresuid },
730 { "clock_gettime", test_clock_gettime },
731 { "gettimeofday", test_gettimeofday },
732 { "getpriority", test_getpriority },
733 { "pipe", test_pipe },
734 { "select", test_select },
735 { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
736 { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
737 { "socketpair_stream", test_socketpair_stream },
738 { "socketpair_dgram", test_socketpair_dgram },
739 { "socket_tcp", test_socket_stream, .t_int = PF_INET },
740 { "socket_udp", test_socket_dgram, .t_int = PF_INET },
741 { "access", test_access, .t_flags = FLAG_PATH },
742 { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
743 { "bad_open", test_bad_open },
744 { "open_close", test_open_close, .t_flags = FLAG_PATH },
745 { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
747 { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
749 { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
751 { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
753 { "open_read_close_10000", test_open_read_close,
754 .t_flags = FLAG_PATH, .t_int = 10000 },
755 { "open_read_close_100000", test_open_read_close,
756 .t_flags = FLAG_PATH, .t_int = 100000 },
757 { "open_read_close_1000000", test_open_read_close,
758 .t_flags = FLAG_PATH, .t_int = 1000000 },
759 { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
760 { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
761 { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
762 { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
763 { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
764 { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
765 { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
767 { "shmfd", test_shmfd },
768 { "fstat_shmfd", test_fstat_shmfd },
769 { "fork", test_fork },
770 { "vfork", test_vfork },
771 { "fork_exec", test_fork_exec },
772 { "vfork_exec", test_vfork_exec },
773 { "chroot", test_chroot },
774 { "setuid", test_setuid },
776 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
783 fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
784 "[-p path] [-s seconds] test\n");
785 for (i = 0; i < tests_count; i++)
786 fprintf(stderr, " %s\n", tests[i].t_name);
791 main(int argc, char *argv[])
793 struct timespec ts_res;
794 const struct test *the_test;
796 char *tmp_dir, *tmp_path;
799 int ch, fd, error, i, j, k, rv;
800 uintmax_t iterations, loops;
807 while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
810 ll = strtol(optarg, &endp, 10);
811 if (*endp != 0 || ll < 1)
817 ll = strtol(optarg, &endp, 10);
818 if (*endp != 0 || ll < 1 || ll > 100000)
828 ll = strtol(optarg, &endp, 10);
829 if (*endp != 0 || ll < 1 || ll > 60*60)
842 if (iterations < 1 && alarm_timeout < 1)
845 iterations = UINT64_MAX;
853 * Validate test list and that, if a path is required, it is
856 for (j = 0; j < argc; j++) {
858 for (i = 0; i < tests_count; i++) {
859 if (strcmp(argv[j], tests[i].t_name) == 0)
860 the_test = &tests[i];
862 if (the_test == NULL)
864 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
865 tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
868 tmp_dir = mkdtemp(tmp_dir);
871 rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
877 error = clock_getres(CLOCK_REALTIME, &ts_res);
879 printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
880 (uintmax_t)ts_res.tv_nsec);
881 printf("test\tloop\ttime\titerations\tperiteration\n");
883 for (j = 0; j < argc; j++) {
884 uintmax_t calls, nsecsperit;
887 for (i = 0; i < tests_count; i++) {
888 if (strcmp(argv[j], tests[i].t_name) == 0)
889 the_test = &tests[i];
892 if (tmp_path != NULL) {
893 fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
895 err(1, "cannot open %s", tmp_path);
896 error = ftruncate(fd, 1000000);
906 * Run one warmup, then do the real thing (loops) times.
908 the_test->t_func(iterations, the_test->t_int, path);
910 for (k = 0; k < loops; k++) {
911 calls = the_test->t_func(iterations, the_test->t_int,
913 timespecsub(&ts_end, &ts_start);
914 printf("%s\t%d\t", the_test->t_name, k);
915 printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
916 (uintmax_t)ts_end.tv_nsec, calls);
919 * Note. This assumes that each iteration takes less than
920 * a second, and that our total nanoseconds doesn't exceed
921 * the room in our arithmetic unit. Fine for system calls,
922 * but not for long things.
924 nsecsperit = ts_end.tv_sec * 1000000000;
925 nsecsperit += ts_end.tv_nsec;
927 printf("0.%09ju\n", (uintmax_t)nsecsperit);
931 if (tmp_path != NULL) {
932 error = unlink(tmp_path);
933 if (error != 0 && errno != ENOENT)
934 warn("cannot unlink %s", tmp_path);
935 error = rmdir(tmp_dir);
937 warn("cannot rmdir %s", tmp_dir);