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
30 #include <sys/param.h>
31 #include <sys/types.h>
33 #include <sys/procdesc.h>
34 #include <sys/resource.h>
35 #include <sys/socket.h>
49 #include <semaphore.h>
56 static struct timespec ts_start, ts_end;
57 static int alarm_timeout;
58 static volatile int alarm_fired;
60 #define BENCHMARK_FOREACH(I, NUM) for (I = 0; I < NUM && alarm_fired == 0; I++)
63 alarm_handler(int signum __unused)
76 signal(SIGALRM, alarm_handler);
79 error = clock_gettime(CLOCK_REALTIME, &ts_start);
88 error = clock_gettime(CLOCK_REALTIME, &ts_end);
93 test_access(uintmax_t num, uintmax_t int_arg __unused, const char *path)
98 fd = access(path, O_RDONLY);
100 err(-1, "test_access: %s", path);
104 BENCHMARK_FOREACH(i, num) {
105 access(path, O_RDONLY);
113 test_bad_open(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
118 BENCHMARK_FOREACH(i, num) {
126 test_chroot(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
131 err(-1, "test_chroot: chroot");
133 BENCHMARK_FOREACH(i, num) {
135 err(-1, "test_chroot: chroot");
142 test_clock_gettime(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
148 BENCHMARK_FOREACH(i, num) {
149 (void)clock_gettime(CLOCK_REALTIME, &ts);
156 test_create_unlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
162 fd = open(path, O_RDWR | O_CREAT, 0600);
164 err(-1, "test_create_unlink: create: %s", path);
166 if (unlink(path) < 0)
167 err(-1, "test_create_unlink: unlink: %s", path);
169 BENCHMARK_FOREACH(i, num) {
170 fd = open(path, O_RDWR | O_CREAT, 0600);
172 err(-1, "test_create_unlink: create: %s", path);
174 if (unlink(path) < 0)
175 err(-1, "test_create_unlink: unlink: %s", path);
182 test_fork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
189 err(-1, "test_fork: fork");
192 if (waitpid(pid, NULL, 0) < 0)
193 err(-1, "test_fork: waitpid");
195 BENCHMARK_FOREACH(i, num) {
198 err(-1, "test_fork: fork");
201 if (waitpid(pid, NULL, 0) < 0)
202 err(-1, "test_fork: waitpid");
208 #define USR_BIN_TRUE "/usr/bin/true"
209 static char *execve_args[] = { __DECONST(char *, USR_BIN_TRUE), NULL};
210 extern char **environ;
213 test_fork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
220 err(-1, "test_fork_exec: fork");
222 (void)execve(USR_BIN_TRUE, execve_args, environ);
225 if (waitpid(pid, NULL, 0) < 0)
226 err(-1, "test_fork: waitpid");
228 BENCHMARK_FOREACH(i, num) {
231 err(-1, "test_fork_exec: fork");
233 (void)execve(USR_BIN_TRUE, execve_args, environ);
234 err(-1, "test_fork_exec: execve");
236 if (waitpid(pid, NULL, 0) < 0)
237 err(-1, "test_fork_exec: waitpid");
244 test_getppid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
249 * This is process-local, but can change, so will require a
253 BENCHMARK_FOREACH(i, num) {
261 test_getpriority(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
266 BENCHMARK_FOREACH(i, num) {
267 (void)getpriority(PRIO_PROCESS, 0);
274 * The point of this one is to figure out the cost of a call into libc,
275 * through PLT, and back.
278 test_getprogname(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
283 BENCHMARK_FOREACH(i, num) {
291 test_getresuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
293 uid_t ruid, euid, suid;
297 BENCHMARK_FOREACH(i, num) {
298 (void)getresuid(&ruid, &euid, &suid);
305 test_gettimeofday(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
311 BENCHMARK_FOREACH(i, num) {
312 (void)gettimeofday(&tv, NULL);
319 test_getuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
324 * Thread-local data should require no locking if system
328 BENCHMARK_FOREACH(i, num) {
336 test_lstat(uintmax_t num, uintmax_t int_arg __unused, const char *path)
343 BENCHMARK_FOREACH(i, num) {
344 error = lstat(path, &sb);
353 test_memcpy(uintmax_t num, uintmax_t int_arg, const char *path __unused)
355 char buf[int_arg], buf2[int_arg];
359 BENCHMARK_FOREACH(i, num) {
361 * Copy the memory there and back, to match the total amount
362 * moved by pipeping/pipepingtd tests.
364 memcpy(buf2, buf, int_arg);
365 memcpy(buf, buf2, int_arg);
373 test_open_close(uintmax_t num, uintmax_t int_arg __unused, const char *path)
378 fd = open(path, O_RDONLY);
380 err(-1, "test_open_close: %s", path);
384 BENCHMARK_FOREACH(i, num) {
385 fd = open(path, O_RDONLY);
387 err(-1, "test_open_close: %s", path);
395 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
401 fd = open(path, O_RDONLY);
403 err(-1, "test_open_read_close: %s", path);
404 (void)read(fd, buf, int_arg);
408 BENCHMARK_FOREACH(i, num) {
409 fd = open(path, O_RDONLY);
411 err(-1, "test_open_read_close: %s", path);
412 (void)read(fd, buf, int_arg);
420 test_pipe(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
426 * pipe creation is expensive, as it will allocate a new file
427 * descriptor, allocate a new pipe, hook it all up, and return.
428 * Destroying is also expensive, as we now have to free up
429 * the file descriptors and return the pipe.
432 err(-1, "test_pipe: pipe");
436 BENCHMARK_FOREACH(i, num) {
438 err(-1, "test_pipe: pipe");
447 readx(int fd, char *buf, size_t size)
452 ret = read(fd, buf, size);
455 assert((size_t)ret <= size);
462 writex(int fd, const char *buf, size_t size)
467 ret = write(fd, buf, size);
470 assert((size_t)ret <= size);
477 test_pipeping(uintmax_t num, uintmax_t int_arg, const char *path __unused)
487 pid = pdfork(&procfd, 0);
495 readx(fd[1], buf, int_arg);
496 writex(fd[1], buf, int_arg);
503 BENCHMARK_FOREACH(i, num) {
504 writex(fd[0], buf, int_arg);
505 readx(fd[0], buf, int_arg);
514 struct pipepingtd_ctx {
520 pipepingtd_proc(void *arg)
522 struct pipepingtd_ctx *ctxp;
529 int_arg = ctxp->int_arg;
531 buf = malloc(int_arg);
536 readx(fd, buf, int_arg);
537 writex(fd, buf, int_arg);
542 test_pipepingtd(uintmax_t num, uintmax_t int_arg, const char *path __unused)
544 struct pipepingtd_ctx ctx;
554 ctx.int_arg = int_arg;
556 error = pthread_create(&td, NULL, pipepingtd_proc, &ctx);
558 err(1, "pthread_create");
561 BENCHMARK_FOREACH(i, num) {
562 writex(fd[0], buf, int_arg);
563 readx(fd[0], buf, int_arg);
570 #endif /* WITH_PTHREAD */
573 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
579 fd = open(path, O_RDONLY);
581 err(-1, "test_open_read: %s", path);
582 (void)pread(fd, buf, int_arg, 0);
585 BENCHMARK_FOREACH(i, num) {
586 (void)pread(fd, buf, int_arg, 0);
594 test_select(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
596 fd_set readfds, writefds, exceptfds;
608 BENCHMARK_FOREACH(i, num) {
609 (void)select(0, &readfds, &writefds, &exceptfds, &tv);
616 test_semaping(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
621 int error, j, procfd;
623 buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
624 if (buf == MAP_FAILED)
627 for (j = 0; j < 2; j++) {
628 error = sem_init(&buf[j], 1, 0);
633 pid = pdfork(&procfd, 0);
639 error = sem_wait(&buf[0]);
642 error = sem_post(&buf[1]);
649 BENCHMARK_FOREACH(i, num) {
650 error = sem_post(&buf[0]);
653 error = sem_wait(&buf[1]);
661 for (j = 0; j < 2; j++) {
662 error = sem_destroy(&buf[j]);
664 err(1, "sem_destroy");
667 error = munmap(buf, PAGE_SIZE);
675 test_setuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
682 err(-1, "test_setuid: setuid");
684 BENCHMARK_FOREACH(i, num) {
686 err(-1, "test_setuid: setuid");
693 test_shmfd(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
698 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
700 err(-1, "test_shmfd: shm_open");
703 BENCHMARK_FOREACH(i, num) {
704 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
706 err(-1, "test_shmfd: shm_open");
714 test_shmfd_dup(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
719 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
721 err(-1, "test_shmfd_dup: shm_open");
726 BENCHMARK_FOREACH(i, num) {
737 test_shmfd_fstat(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
743 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
745 err(-1, "test_shmfd_fstat: shm_open");
746 if (fstat(shmfd, &sb) < 0)
747 err(-1, "test_shmfd_fstat: fstat");
749 BENCHMARK_FOREACH(i, num) {
750 (void)fstat(shmfd, &sb);
758 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path __unused)
763 so = socket(int_arg, SOCK_STREAM, 0);
765 err(-1, "test_socket_stream: socket");
768 BENCHMARK_FOREACH(i, num) {
769 so = socket(int_arg, SOCK_STREAM, 0);
771 err(-1, "test_socket_stream: socket");
779 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path __unused)
784 so = socket(int_arg, SOCK_DGRAM, 0);
786 err(-1, "test_socket_dgram: socket");
789 BENCHMARK_FOREACH(i, num) {
790 so = socket(int_arg, SOCK_DGRAM, 0);
792 err(-1, "test_socket_dgram: socket");
800 test_socketpair_stream(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
805 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
806 err(-1, "test_socketpair_stream: socketpair");
810 BENCHMARK_FOREACH(i, num) {
811 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
812 err(-1, "test_socketpair_stream: socketpair");
821 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
826 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
827 err(-1, "test_socketpair_dgram: socketpair");
831 BENCHMARK_FOREACH(i, num) {
832 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
833 err(-1, "test_socketpair_dgram: socketpair");
842 test_readlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
849 BENCHMARK_FOREACH(i, num) {
850 rv = readlink(path, buf, sizeof(buf));
851 if (rv < 0 && errno != EINVAL)
859 test_vfork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
866 err(-1, "test_vfork: vfork");
869 if (waitpid(pid, NULL, 0) < 0)
870 err(-1, "test_vfork: waitpid");
872 BENCHMARK_FOREACH(i, num) {
875 err(-1, "test_vfork: vfork");
878 if (waitpid(pid, NULL, 0) < 0)
879 err(-1, "test_vfork: waitpid");
886 test_vfork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
893 err(-1, "test_vfork_exec: vfork");
895 (void)execve(USR_BIN_TRUE, execve_args, environ);
896 err(-1, "test_vfork_exec: execve");
898 if (waitpid(pid, NULL, 0) < 0)
899 err(-1, "test_vfork_exec: waitpid");
901 BENCHMARK_FOREACH(i, num) {
904 err(-1, "test_vfork_exec: vfork");
906 (void)execve(USR_BIN_TRUE, execve_args, environ);
909 if (waitpid(pid, NULL, 0) < 0)
910 err(-1, "test_vfork_exec: waitpid");
918 uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *);
923 #define FLAG_PATH 0x00000001
925 static const struct test tests[] = {
926 { "access", test_access, .t_flags = FLAG_PATH },
927 { "bad_open", test_bad_open, .t_flags = 0 },
928 { "chroot", test_chroot, .t_flags = 0 },
929 { "clock_gettime", test_clock_gettime, .t_flags = 0 },
930 { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
931 { "fork", test_fork, .t_flags = 0 },
932 { "fork_exec", test_fork_exec, .t_flags = 0 },
933 { "getppid", test_getppid, .t_flags = 0 },
934 { "getpriority", test_getpriority, .t_flags = 0 },
935 { "getprogname", test_getprogname, .t_flags = 0 },
936 { "getresuid", test_getresuid, .t_flags = 0 },
937 { "gettimeofday", test_gettimeofday, .t_flags = 0 },
938 { "getuid", test_getuid, .t_flags = 0 },
939 { "lstat", test_lstat, .t_flags = FLAG_PATH },
940 { "memcpy_1", test_memcpy, .t_flags = 0, .t_int = 1 },
941 { "memcpy_10", test_memcpy, .t_flags = 0, .t_int = 10 },
942 { "memcpy_100", test_memcpy, .t_flags = 0, .t_int = 100 },
943 { "memcpy_1000", test_memcpy, .t_flags = 0, .t_int = 1000 },
944 { "memcpy_10000", test_memcpy, .t_flags = 0, .t_int = 10000 },
945 { "memcpy_100000", test_memcpy, .t_flags = 0, .t_int = 100000 },
946 { "memcpy_1000000", test_memcpy, .t_flags = 0, .t_int = 1000000 },
947 { "open_close", test_open_close, .t_flags = FLAG_PATH },
948 { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
950 { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
952 { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
954 { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
956 { "open_read_close_10000", test_open_read_close,
957 .t_flags = FLAG_PATH, .t_int = 10000 },
958 { "open_read_close_100000", test_open_read_close,
959 .t_flags = FLAG_PATH, .t_int = 100000 },
960 { "open_read_close_1000000", test_open_read_close,
961 .t_flags = FLAG_PATH, .t_int = 1000000 },
962 { "pipe", test_pipe, .t_flags = 0 },
963 { "pipeping_1", test_pipeping, .t_flags = 0, .t_int = 1 },
964 { "pipeping_10", test_pipeping, .t_flags = 0, .t_int = 10 },
965 { "pipeping_100", test_pipeping, .t_flags = 0, .t_int = 100 },
966 { "pipeping_1000", test_pipeping, .t_flags = 0, .t_int = 1000 },
967 { "pipeping_10000", test_pipeping, .t_flags = 0, .t_int = 10000 },
968 { "pipeping_100000", test_pipeping, .t_flags = 0, .t_int = 100000 },
969 { "pipeping_1000000", test_pipeping, .t_flags = 0, .t_int = 1000000 },
971 { "pipepingtd_1", test_pipepingtd, .t_flags = 0, .t_int = 1 },
972 { "pipepingtd_10", test_pipepingtd, .t_flags = 0, .t_int = 10 },
973 { "pipepingtd_100", test_pipepingtd, .t_flags = 0, .t_int = 100 },
974 { "pipepingtd_1000", test_pipepingtd, .t_flags = 0, .t_int = 1000 },
975 { "pipepingtd_10000", test_pipepingtd, .t_flags = 0, .t_int = 10000 },
976 { "pipepingtd_100000", test_pipepingtd, .t_flags = 0, .t_int = 100000 },
977 { "pipepingtd_1000000", test_pipepingtd, .t_flags = 0, .t_int = 1000000 },
979 { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
980 { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
981 { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
982 { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
983 { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
984 { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
985 { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
986 { "select", test_select, .t_flags = 0 },
987 { "semaping", test_semaping, .t_flags = 0 },
988 { "setuid", test_setuid, .t_flags = 0 },
989 { "shmfd", test_shmfd, .t_flags = 0 },
990 { "shmfd_dup", test_shmfd_dup, .t_flags = 0 },
991 { "shmfd_fstat", test_shmfd_fstat, .t_flags = 0 },
992 { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
993 { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
994 { "socketpair_stream", test_socketpair_stream, .t_flags = 0 },
995 { "socketpair_dgram", test_socketpair_dgram, .t_flags = 0 },
996 { "socket_tcp", test_socket_stream, .t_int = PF_INET },
997 { "socket_udp", test_socket_dgram, .t_int = PF_INET },
998 { "readlink", test_readlink, .t_flags = FLAG_PATH },
999 { "vfork", test_vfork, .t_flags = 0 },
1000 { "vfork_exec", test_vfork_exec, .t_flags = 0 },
1002 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
1009 fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
1010 "[-p path] [-s seconds] test\n");
1011 for (i = 0; i < tests_count; i++)
1012 fprintf(stderr, " %s\n", tests[i].t_name);
1017 main(int argc, char *argv[])
1019 struct timespec ts_res;
1020 const struct test *the_test;
1022 char *tmp_dir, *tmp_path;
1025 int ch, fd, error, i, j, rv;
1026 uintmax_t iterations, k, loops;
1033 while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
1036 ll = strtol(optarg, &endp, 10);
1037 if (*endp != 0 || ll < 1)
1043 ll = strtol(optarg, &endp, 10);
1044 if (*endp != 0 || ll < 1 || ll > 100000)
1054 ll = strtol(optarg, &endp, 10);
1055 if (*endp != 0 || ll < 1 || ll > 60*60)
1068 if (iterations < 1 && alarm_timeout < 1)
1071 iterations = UINT64_MAX;
1079 * Validate test list and that, if a path is required, it is
1082 for (j = 0; j < argc; j++) {
1084 for (i = 0; i < tests_count; i++) {
1085 if (strcmp(argv[j], tests[i].t_name) == 0)
1086 the_test = &tests[i];
1088 if (the_test == NULL)
1090 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
1091 tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
1092 if (tmp_dir == NULL)
1094 tmp_dir = mkdtemp(tmp_dir);
1095 if (tmp_dir == NULL)
1097 rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
1103 error = clock_getres(CLOCK_REALTIME, &ts_res);
1105 printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
1106 (uintmax_t)ts_res.tv_nsec);
1107 printf("test\tloop\ttime\titerations\tperiteration\n");
1109 for (j = 0; j < argc; j++) {
1110 uintmax_t calls, nsecsperit;
1113 for (i = 0; i < tests_count; i++) {
1114 if (strcmp(argv[j], tests[i].t_name) == 0)
1115 the_test = &tests[i];
1118 if (tmp_path != NULL) {
1119 fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
1121 err(1, "cannot open %s", tmp_path);
1122 error = ftruncate(fd, 1000000);
1124 err(1, "ftruncate");
1132 * Run one warmup, then do the real thing (loops) times.
1134 the_test->t_func(iterations, the_test->t_int, path);
1136 for (k = 0; k < loops; k++) {
1137 calls = the_test->t_func(iterations, the_test->t_int,
1139 timespecsub(&ts_end, &ts_start, &ts_end);
1140 printf("%s\t%ju\t", the_test->t_name, k);
1141 printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
1142 (uintmax_t)ts_end.tv_nsec, calls);
1145 * Note. This assumes that each iteration takes less than
1146 * a second, and that our total nanoseconds doesn't exceed
1147 * the room in our arithmetic unit. Fine for system calls,
1148 * but not for long things.
1150 nsecsperit = ts_end.tv_sec * 1000000000;
1151 nsecsperit += ts_end.tv_nsec;
1152 nsecsperit /= calls;
1153 printf("0.%09ju\n", (uintmax_t)nsecsperit);
1157 if (tmp_path != NULL) {
1158 error = unlink(tmp_path);
1159 if (error != 0 && errno != ENOENT)
1160 warn("cannot unlink %s", tmp_path);
1161 error = rmdir(tmp_dir);
1163 warn("cannot rmdir %s", tmp_dir);