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/param.h>
33 #include <sys/types.h>
35 #include <sys/procdesc.h>
36 #include <sys/resource.h>
37 #include <sys/socket.h>
51 #include <semaphore.h>
58 static struct timespec ts_start, ts_end;
59 static int alarm_timeout;
60 static volatile int alarm_fired;
62 #define BENCHMARK_FOREACH(I, NUM) for (I = 0; I < NUM && alarm_fired == 0; I++)
65 alarm_handler(int signum __unused)
78 signal(SIGALRM, alarm_handler);
81 error = clock_gettime(CLOCK_REALTIME, &ts_start);
90 error = clock_gettime(CLOCK_REALTIME, &ts_end);
95 test_access(uintmax_t num, uintmax_t int_arg __unused, const char *path)
100 fd = access(path, O_RDONLY);
102 err(-1, "test_access: %s", path);
106 BENCHMARK_FOREACH(i, num) {
107 access(path, O_RDONLY);
115 test_bad_open(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
120 BENCHMARK_FOREACH(i, num) {
128 test_chroot(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
133 err(-1, "test_chroot: chroot");
135 BENCHMARK_FOREACH(i, num) {
137 err(-1, "test_chroot: chroot");
144 test_clock_gettime(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
150 BENCHMARK_FOREACH(i, num) {
151 (void)clock_gettime(CLOCK_REALTIME, &ts);
158 test_create_unlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
164 fd = open(path, O_RDWR | O_CREAT, 0600);
166 err(-1, "test_create_unlink: create: %s", path);
168 if (unlink(path) < 0)
169 err(-1, "test_create_unlink: unlink: %s", path);
171 BENCHMARK_FOREACH(i, num) {
172 fd = open(path, O_RDWR | O_CREAT, 0600);
174 err(-1, "test_create_unlink: create: %s", path);
176 if (unlink(path) < 0)
177 err(-1, "test_create_unlink: unlink: %s", path);
184 test_fork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
191 err(-1, "test_fork: fork");
194 if (waitpid(pid, NULL, 0) < 0)
195 err(-1, "test_fork: waitpid");
197 BENCHMARK_FOREACH(i, num) {
200 err(-1, "test_fork: fork");
203 if (waitpid(pid, NULL, 0) < 0)
204 err(-1, "test_fork: waitpid");
210 #define USR_BIN_TRUE "/usr/bin/true"
211 static char *execve_args[] = { __DECONST(char *, USR_BIN_TRUE), NULL};
212 extern char **environ;
215 test_fork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
222 err(-1, "test_fork_exec: fork");
224 (void)execve(USR_BIN_TRUE, execve_args, environ);
227 if (waitpid(pid, NULL, 0) < 0)
228 err(-1, "test_fork: waitpid");
230 BENCHMARK_FOREACH(i, num) {
233 err(-1, "test_fork_exec: fork");
235 (void)execve(USR_BIN_TRUE, execve_args, environ);
236 err(-1, "test_fork_exec: execve");
238 if (waitpid(pid, NULL, 0) < 0)
239 err(-1, "test_fork_exec: waitpid");
246 test_getppid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
251 * This is process-local, but can change, so will require a
255 BENCHMARK_FOREACH(i, num) {
263 test_getpriority(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
268 BENCHMARK_FOREACH(i, num) {
269 (void)getpriority(PRIO_PROCESS, 0);
276 * The point of this one is to figure out the cost of a call into libc,
277 * through PLT, and back.
280 test_getprogname(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
285 BENCHMARK_FOREACH(i, num) {
293 test_getresuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
295 uid_t ruid, euid, suid;
299 BENCHMARK_FOREACH(i, num) {
300 (void)getresuid(&ruid, &euid, &suid);
307 test_gettimeofday(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
313 BENCHMARK_FOREACH(i, num) {
314 (void)gettimeofday(&tv, NULL);
321 test_getuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
326 * Thread-local data should require no locking if system
330 BENCHMARK_FOREACH(i, num) {
338 test_memcpy(uintmax_t num, uintmax_t int_arg, const char *path __unused)
340 char buf[int_arg], buf2[int_arg];
344 BENCHMARK_FOREACH(i, num) {
346 * Copy the memory there and back, to match the total amount
347 * moved by pipeping/pipepingtd tests.
349 memcpy(buf2, buf, int_arg);
350 memcpy(buf, buf2, int_arg);
358 test_open_close(uintmax_t num, uintmax_t int_arg __unused, const char *path)
363 fd = open(path, O_RDONLY);
365 err(-1, "test_open_close: %s", path);
369 BENCHMARK_FOREACH(i, num) {
370 fd = open(path, O_RDONLY);
372 err(-1, "test_open_close: %s", path);
380 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
386 fd = open(path, O_RDONLY);
388 err(-1, "test_open_read_close: %s", path);
389 (void)read(fd, buf, int_arg);
393 BENCHMARK_FOREACH(i, num) {
394 fd = open(path, O_RDONLY);
396 err(-1, "test_open_read_close: %s", path);
397 (void)read(fd, buf, int_arg);
405 test_pipe(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
411 * pipe creation is expensive, as it will allocate a new file
412 * descriptor, allocate a new pipe, hook it all up, and return.
413 * Destroying is also expensive, as we now have to free up
414 * the file descriptors and return the pipe.
417 err(-1, "test_pipe: pipe");
421 BENCHMARK_FOREACH(i, num) {
423 err(-1, "test_pipe: pipe");
432 readx(int fd, char *buf, size_t size)
437 ret = read(fd, buf, size);
440 assert((size_t)ret <= size);
447 writex(int fd, const char *buf, size_t size)
452 ret = write(fd, buf, size);
455 assert((size_t)ret <= size);
462 test_pipeping(uintmax_t num, uintmax_t int_arg, const char *path __unused)
472 pid = pdfork(&procfd, 0);
480 readx(fd[1], buf, int_arg);
481 writex(fd[1], buf, int_arg);
488 BENCHMARK_FOREACH(i, num) {
489 writex(fd[0], buf, int_arg);
490 readx(fd[0], buf, int_arg);
499 struct pipepingtd_ctx {
505 pipepingtd_proc(void *arg)
507 struct pipepingtd_ctx *ctxp;
514 int_arg = ctxp->int_arg;
516 buf = malloc(int_arg);
521 readx(fd, buf, int_arg);
522 writex(fd, buf, int_arg);
527 test_pipepingtd(uintmax_t num, uintmax_t int_arg, const char *path __unused)
529 struct pipepingtd_ctx ctx;
539 ctx.int_arg = int_arg;
541 error = pthread_create(&td, NULL, pipepingtd_proc, &ctx);
543 err(1, "pthread_create");
546 BENCHMARK_FOREACH(i, num) {
547 writex(fd[0], buf, int_arg);
548 readx(fd[0], buf, int_arg);
555 #endif /* WITH_PTHREAD */
558 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
564 fd = open(path, O_RDONLY);
566 err(-1, "test_open_read: %s", path);
567 (void)pread(fd, buf, int_arg, 0);
570 BENCHMARK_FOREACH(i, num) {
571 (void)pread(fd, buf, int_arg, 0);
579 test_select(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
581 fd_set readfds, writefds, exceptfds;
593 BENCHMARK_FOREACH(i, num) {
594 (void)select(0, &readfds, &writefds, &exceptfds, &tv);
601 test_semaping(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
606 int error, j, procfd;
608 buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
609 if (buf == MAP_FAILED)
612 for (j = 0; j < 2; j++) {
613 error = sem_init(&buf[j], 1, 0);
618 pid = pdfork(&procfd, 0);
624 error = sem_wait(&buf[0]);
627 error = sem_post(&buf[1]);
634 BENCHMARK_FOREACH(i, num) {
635 error = sem_post(&buf[0]);
638 error = sem_wait(&buf[1]);
646 for (j = 0; j < 2; j++) {
647 error = sem_destroy(&buf[j]);
649 err(1, "sem_destroy");
652 error = munmap(buf, PAGE_SIZE);
660 test_setuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
667 err(-1, "test_setuid: setuid");
669 BENCHMARK_FOREACH(i, num) {
671 err(-1, "test_setuid: setuid");
678 test_shmfd(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
683 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
685 err(-1, "test_shmfd: shm_open");
688 BENCHMARK_FOREACH(i, num) {
689 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
691 err(-1, "test_shmfd: shm_open");
699 test_shmfd_dup(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
704 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
706 err(-1, "test_shmfd_dup: shm_open");
711 BENCHMARK_FOREACH(i, num) {
722 test_shmfd_fstat(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
728 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
730 err(-1, "test_shmfd_fstat: shm_open");
731 if (fstat(shmfd, &sb) < 0)
732 err(-1, "test_shmfd_fstat: fstat");
734 BENCHMARK_FOREACH(i, num) {
735 (void)fstat(shmfd, &sb);
743 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path __unused)
748 so = socket(int_arg, SOCK_STREAM, 0);
750 err(-1, "test_socket_stream: socket");
753 BENCHMARK_FOREACH(i, num) {
754 so = socket(int_arg, SOCK_STREAM, 0);
756 err(-1, "test_socket_stream: socket");
764 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path __unused)
769 so = socket(int_arg, SOCK_DGRAM, 0);
771 err(-1, "test_socket_dgram: socket");
774 BENCHMARK_FOREACH(i, num) {
775 so = socket(int_arg, SOCK_DGRAM, 0);
777 err(-1, "test_socket_dgram: socket");
785 test_socketpair_stream(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
790 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
791 err(-1, "test_socketpair_stream: socketpair");
795 BENCHMARK_FOREACH(i, num) {
796 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
797 err(-1, "test_socketpair_stream: socketpair");
806 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
811 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
812 err(-1, "test_socketpair_dgram: socketpair");
816 BENCHMARK_FOREACH(i, num) {
817 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
818 err(-1, "test_socketpair_dgram: socketpair");
827 test_vfork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
834 err(-1, "test_vfork: vfork");
837 if (waitpid(pid, NULL, 0) < 0)
838 err(-1, "test_vfork: waitpid");
840 BENCHMARK_FOREACH(i, num) {
843 err(-1, "test_vfork: vfork");
846 if (waitpid(pid, NULL, 0) < 0)
847 err(-1, "test_vfork: waitpid");
854 test_vfork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
861 err(-1, "test_vfork_exec: vfork");
863 (void)execve(USR_BIN_TRUE, execve_args, environ);
864 err(-1, "test_vfork_exec: execve");
866 if (waitpid(pid, NULL, 0) < 0)
867 err(-1, "test_vfork_exec: waitpid");
869 BENCHMARK_FOREACH(i, num) {
872 err(-1, "test_vfork_exec: vfork");
874 (void)execve(USR_BIN_TRUE, execve_args, environ);
877 if (waitpid(pid, NULL, 0) < 0)
878 err(-1, "test_vfork_exec: waitpid");
886 uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *);
891 #define FLAG_PATH 0x00000001
893 static const struct test tests[] = {
894 { "access", test_access, .t_flags = FLAG_PATH },
895 { "bad_open", test_bad_open, .t_flags = 0 },
896 { "chroot", test_chroot, .t_flags = 0 },
897 { "clock_gettime", test_clock_gettime, .t_flags = 0 },
898 { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
899 { "fork", test_fork, .t_flags = 0 },
900 { "fork_exec", test_fork_exec, .t_flags = 0 },
901 { "getppid", test_getppid, .t_flags = 0 },
902 { "getpriority", test_getpriority, .t_flags = 0 },
903 { "getprogname", test_getprogname, .t_flags = 0 },
904 { "getresuid", test_getresuid, .t_flags = 0 },
905 { "gettimeofday", test_gettimeofday, .t_flags = 0 },
906 { "getuid", test_getuid, .t_flags = 0 },
907 { "memcpy_1", test_memcpy, .t_flags = 0, .t_int = 1 },
908 { "memcpy_10", test_memcpy, .t_flags = 0, .t_int = 10 },
909 { "memcpy_100", test_memcpy, .t_flags = 0, .t_int = 100 },
910 { "memcpy_1000", test_memcpy, .t_flags = 0, .t_int = 1000 },
911 { "memcpy_10000", test_memcpy, .t_flags = 0, .t_int = 10000 },
912 { "memcpy_100000", test_memcpy, .t_flags = 0, .t_int = 100000 },
913 { "memcpy_1000000", test_memcpy, .t_flags = 0, .t_int = 1000000 },
914 { "open_close", test_open_close, .t_flags = FLAG_PATH },
915 { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
917 { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
919 { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
921 { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
923 { "open_read_close_10000", test_open_read_close,
924 .t_flags = FLAG_PATH, .t_int = 10000 },
925 { "open_read_close_100000", test_open_read_close,
926 .t_flags = FLAG_PATH, .t_int = 100000 },
927 { "open_read_close_1000000", test_open_read_close,
928 .t_flags = FLAG_PATH, .t_int = 1000000 },
929 { "pipe", test_pipe, .t_flags = 0 },
930 { "pipeping_1", test_pipeping, .t_flags = 0, .t_int = 1 },
931 { "pipeping_10", test_pipeping, .t_flags = 0, .t_int = 10 },
932 { "pipeping_100", test_pipeping, .t_flags = 0, .t_int = 100 },
933 { "pipeping_1000", test_pipeping, .t_flags = 0, .t_int = 1000 },
934 { "pipeping_10000", test_pipeping, .t_flags = 0, .t_int = 10000 },
935 { "pipeping_100000", test_pipeping, .t_flags = 0, .t_int = 100000 },
936 { "pipeping_1000000", test_pipeping, .t_flags = 0, .t_int = 1000000 },
938 { "pipepingtd_1", test_pipepingtd, .t_flags = 0, .t_int = 1 },
939 { "pipepingtd_10", test_pipepingtd, .t_flags = 0, .t_int = 10 },
940 { "pipepingtd_100", test_pipepingtd, .t_flags = 0, .t_int = 100 },
941 { "pipepingtd_1000", test_pipepingtd, .t_flags = 0, .t_int = 1000 },
942 { "pipepingtd_10000", test_pipepingtd, .t_flags = 0, .t_int = 10000 },
943 { "pipepingtd_100000", test_pipepingtd, .t_flags = 0, .t_int = 100000 },
944 { "pipepingtd_1000000", test_pipepingtd, .t_flags = 0, .t_int = 1000000 },
946 { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
947 { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
948 { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
949 { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
950 { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
951 { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
952 { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
953 { "select", test_select, .t_flags = 0 },
954 { "semaping", test_semaping, .t_flags = 0 },
955 { "setuid", test_setuid, .t_flags = 0 },
956 { "shmfd", test_shmfd, .t_flags = 0 },
957 { "shmfd_dup", test_shmfd_dup, .t_flags = 0 },
958 { "shmfd_fstat", test_shmfd_fstat, .t_flags = 0 },
959 { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
960 { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
961 { "socketpair_stream", test_socketpair_stream, .t_flags = 0 },
962 { "socketpair_dgram", test_socketpair_dgram, .t_flags = 0 },
963 { "socket_tcp", test_socket_stream, .t_int = PF_INET },
964 { "socket_udp", test_socket_dgram, .t_int = PF_INET },
965 { "vfork", test_vfork, .t_flags = 0 },
966 { "vfork_exec", test_vfork_exec, .t_flags = 0 },
968 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
975 fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
976 "[-p path] [-s seconds] test\n");
977 for (i = 0; i < tests_count; i++)
978 fprintf(stderr, " %s\n", tests[i].t_name);
983 main(int argc, char *argv[])
985 struct timespec ts_res;
986 const struct test *the_test;
988 char *tmp_dir, *tmp_path;
991 int ch, fd, error, i, j, rv;
992 uintmax_t iterations, k, loops;
999 while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
1002 ll = strtol(optarg, &endp, 10);
1003 if (*endp != 0 || ll < 1)
1009 ll = strtol(optarg, &endp, 10);
1010 if (*endp != 0 || ll < 1 || ll > 100000)
1020 ll = strtol(optarg, &endp, 10);
1021 if (*endp != 0 || ll < 1 || ll > 60*60)
1034 if (iterations < 1 && alarm_timeout < 1)
1037 iterations = UINT64_MAX;
1045 * Validate test list and that, if a path is required, it is
1048 for (j = 0; j < argc; j++) {
1050 for (i = 0; i < tests_count; i++) {
1051 if (strcmp(argv[j], tests[i].t_name) == 0)
1052 the_test = &tests[i];
1054 if (the_test == NULL)
1056 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
1057 tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
1058 if (tmp_dir == NULL)
1060 tmp_dir = mkdtemp(tmp_dir);
1061 if (tmp_dir == NULL)
1063 rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
1069 error = clock_getres(CLOCK_REALTIME, &ts_res);
1071 printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
1072 (uintmax_t)ts_res.tv_nsec);
1073 printf("test\tloop\ttime\titerations\tperiteration\n");
1075 for (j = 0; j < argc; j++) {
1076 uintmax_t calls, nsecsperit;
1079 for (i = 0; i < tests_count; i++) {
1080 if (strcmp(argv[j], tests[i].t_name) == 0)
1081 the_test = &tests[i];
1084 if (tmp_path != NULL) {
1085 fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
1087 err(1, "cannot open %s", tmp_path);
1088 error = ftruncate(fd, 1000000);
1090 err(1, "ftruncate");
1098 * Run one warmup, then do the real thing (loops) times.
1100 the_test->t_func(iterations, the_test->t_int, path);
1102 for (k = 0; k < loops; k++) {
1103 calls = the_test->t_func(iterations, the_test->t_int,
1105 timespecsub(&ts_end, &ts_start, &ts_end);
1106 printf("%s\t%ju\t", the_test->t_name, k);
1107 printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
1108 (uintmax_t)ts_end.tv_nsec, calls);
1111 * Note. This assumes that each iteration takes less than
1112 * a second, and that our total nanoseconds doesn't exceed
1113 * the room in our arithmetic unit. Fine for system calls,
1114 * but not for long things.
1116 nsecsperit = ts_end.tv_sec * 1000000000;
1117 nsecsperit += ts_end.tv_nsec;
1118 nsecsperit /= calls;
1119 printf("0.%09ju\n", (uintmax_t)nsecsperit);
1123 if (tmp_path != NULL) {
1124 error = unlink(tmp_path);
1125 if (error != 0 && errno != ENOENT)
1126 warn("cannot unlink %s", tmp_path);
1127 error = rmdir(tmp_dir);
1129 warn("cannot rmdir %s", tmp_dir);