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
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 #include <sys/procdesc.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 #ifdef WITH_PTHREAD
47 #include <pthread.h>
48 #endif
49 #include <semaphore.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
55
56 static struct timespec ts_start, ts_end;
57 static int alarm_timeout;
58 static volatile int alarm_fired;
59
60 #define BENCHMARK_FOREACH(I, NUM) for (I = 0; I < NUM && alarm_fired == 0; I++)
61
62 static void
alarm_handler(int signum __unused)63 alarm_handler(int signum __unused)
64 {
65
66 alarm_fired = 1;
67 }
68
69 static void
benchmark_start(void)70 benchmark_start(void)
71 {
72 int error;
73
74 alarm_fired = 0;
75 if (alarm_timeout) {
76 signal(SIGALRM, alarm_handler);
77 alarm(alarm_timeout);
78 }
79 error = clock_gettime(CLOCK_REALTIME, &ts_start);
80 assert(error == 0);
81 }
82
83 static void
benchmark_stop(void)84 benchmark_stop(void)
85 {
86 int error;
87
88 error = clock_gettime(CLOCK_REALTIME, &ts_end);
89 assert(error == 0);
90 }
91
92 static uintmax_t
test_access(uintmax_t num,uintmax_t int_arg __unused,const char * path)93 test_access(uintmax_t num, uintmax_t int_arg __unused, const char *path)
94 {
95 uintmax_t i;
96 int fd;
97
98 fd = access(path, O_RDONLY);
99 if (fd < 0)
100 err(-1, "test_access: %s", path);
101 close(fd);
102
103 benchmark_start();
104 BENCHMARK_FOREACH(i, num) {
105 access(path, O_RDONLY);
106 close(fd);
107 }
108 benchmark_stop();
109 return (i);
110 }
111
112 static uintmax_t
test_bad_open(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)113 test_bad_open(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
114 {
115 uintmax_t i;
116
117 benchmark_start();
118 BENCHMARK_FOREACH(i, num) {
119 open("", O_RDONLY);
120 }
121 benchmark_stop();
122 return (i);
123 }
124
125 static uintmax_t
test_chroot(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)126 test_chroot(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
127 {
128 uintmax_t i;
129
130 if (chroot("/") < 0)
131 err(-1, "test_chroot: chroot");
132 benchmark_start();
133 BENCHMARK_FOREACH(i, num) {
134 if (chroot("/") < 0)
135 err(-1, "test_chroot: chroot");
136 }
137 benchmark_stop();
138 return (i);
139 }
140
141 static uintmax_t
test_clock_gettime(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)142 test_clock_gettime(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
143 {
144 struct timespec ts;
145 uintmax_t i;
146
147 benchmark_start();
148 BENCHMARK_FOREACH(i, num) {
149 (void)clock_gettime(CLOCK_REALTIME, &ts);
150 }
151 benchmark_stop();
152 return (i);
153 }
154
155 static uintmax_t
test_create_unlink(uintmax_t num,uintmax_t int_arg __unused,const char * path)156 test_create_unlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
157 {
158 uintmax_t i;
159 int fd;
160
161 (void)unlink(path);
162 fd = open(path, O_RDWR | O_CREAT, 0600);
163 if (fd < 0)
164 err(-1, "test_create_unlink: create: %s", path);
165 close(fd);
166 if (unlink(path) < 0)
167 err(-1, "test_create_unlink: unlink: %s", path);
168 benchmark_start();
169 BENCHMARK_FOREACH(i, num) {
170 fd = open(path, O_RDWR | O_CREAT, 0600);
171 if (fd < 0)
172 err(-1, "test_create_unlink: create: %s", path);
173 close(fd);
174 if (unlink(path) < 0)
175 err(-1, "test_create_unlink: unlink: %s", path);
176 }
177 benchmark_stop();
178 return (i);
179 }
180
181 static uintmax_t
test_fork(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)182 test_fork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
183 {
184 pid_t pid;
185 uintmax_t i;
186
187 pid = fork();
188 if (pid < 0)
189 err(-1, "test_fork: fork");
190 if (pid == 0)
191 _exit(0);
192 if (waitpid(pid, NULL, 0) < 0)
193 err(-1, "test_fork: waitpid");
194 benchmark_start();
195 BENCHMARK_FOREACH(i, num) {
196 pid = fork();
197 if (pid < 0)
198 err(-1, "test_fork: fork");
199 if (pid == 0)
200 _exit(0);
201 if (waitpid(pid, NULL, 0) < 0)
202 err(-1, "test_fork: waitpid");
203 }
204 benchmark_stop();
205 return (i);
206 }
207
208 #define USR_BIN_TRUE "/usr/bin/true"
209 static char *execve_args[] = { __DECONST(char *, USR_BIN_TRUE), NULL};
210 extern char **environ;
211
212 static uintmax_t
test_fork_exec(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)213 test_fork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
214 {
215 pid_t pid;
216 uintmax_t i;
217
218 pid = fork();
219 if (pid < 0)
220 err(-1, "test_fork_exec: fork");
221 if (pid == 0) {
222 (void)execve(USR_BIN_TRUE, execve_args, environ);
223 err(-1, "execve");
224 }
225 if (waitpid(pid, NULL, 0) < 0)
226 err(-1, "test_fork: waitpid");
227 benchmark_start();
228 BENCHMARK_FOREACH(i, num) {
229 pid = fork();
230 if (pid < 0)
231 err(-1, "test_fork_exec: fork");
232 if (pid == 0) {
233 (void)execve(USR_BIN_TRUE, execve_args, environ);
234 err(-1, "test_fork_exec: execve");
235 }
236 if (waitpid(pid, NULL, 0) < 0)
237 err(-1, "test_fork_exec: waitpid");
238 }
239 benchmark_stop();
240 return (i);
241 }
242
243 static uintmax_t
test_getppid(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)244 test_getppid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
245 {
246 uintmax_t i;
247
248 /*
249 * This is process-local, but can change, so will require a
250 * lock.
251 */
252 benchmark_start();
253 BENCHMARK_FOREACH(i, num) {
254 getppid();
255 }
256 benchmark_stop();
257 return (i);
258 }
259
260 static uintmax_t
test_getpriority(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)261 test_getpriority(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
262 {
263 uintmax_t i;
264
265 benchmark_start();
266 BENCHMARK_FOREACH(i, num) {
267 (void)getpriority(PRIO_PROCESS, 0);
268 }
269 benchmark_stop();
270 return (i);
271 }
272
273 /*
274 * The point of this one is to figure out the cost of a call into libc,
275 * through PLT, and back.
276 */
277 static uintmax_t
test_getprogname(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)278 test_getprogname(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
279 {
280 uintmax_t i;
281
282 benchmark_start();
283 BENCHMARK_FOREACH(i, num) {
284 (void)getprogname();
285 }
286 benchmark_stop();
287 return (i);
288 }
289
290 static uintmax_t
test_getresuid(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)291 test_getresuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
292 {
293 uid_t ruid, euid, suid;
294 uintmax_t i;
295
296 benchmark_start();
297 BENCHMARK_FOREACH(i, num) {
298 (void)getresuid(&ruid, &euid, &suid);
299 }
300 benchmark_stop();
301 return (i);
302 }
303
304 static uintmax_t
test_gettimeofday(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)305 test_gettimeofday(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
306 {
307 struct timeval tv;
308 uintmax_t i;
309
310 benchmark_start();
311 BENCHMARK_FOREACH(i, num) {
312 (void)gettimeofday(&tv, NULL);
313 }
314 benchmark_stop();
315 return (i);
316 }
317
318 static uintmax_t
test_getuid(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)319 test_getuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
320 {
321 uintmax_t i;
322
323 /*
324 * Thread-local data should require no locking if system
325 * call is MPSAFE.
326 */
327 benchmark_start();
328 BENCHMARK_FOREACH(i, num) {
329 getuid();
330 }
331 benchmark_stop();
332 return (i);
333 }
334
335 static uintmax_t
test_lstat(uintmax_t num,uintmax_t int_arg __unused,const char * path)336 test_lstat(uintmax_t num, uintmax_t int_arg __unused, const char *path)
337 {
338 struct stat sb;
339 uintmax_t i;
340 int error;
341
342 benchmark_start();
343 BENCHMARK_FOREACH(i, num) {
344 error = lstat(path, &sb);
345 if (error != 0)
346 err(-1, "lstat");
347 }
348 benchmark_stop();
349 return (i);
350 }
351
352 static uintmax_t
test_memcpy(uintmax_t num,uintmax_t int_arg,const char * path __unused)353 test_memcpy(uintmax_t num, uintmax_t int_arg, const char *path __unused)
354 {
355 char buf[int_arg], buf2[int_arg];
356 uintmax_t i;
357
358 benchmark_start();
359 BENCHMARK_FOREACH(i, num) {
360 /*
361 * Copy the memory there and back, to match the total amount
362 * moved by pipeping/pipepingtd tests.
363 */
364 memcpy(buf2, buf, int_arg);
365 memcpy(buf, buf2, int_arg);
366 }
367 benchmark_stop();
368
369 return (i);
370 }
371
372 static uintmax_t
test_open_close(uintmax_t num,uintmax_t int_arg __unused,const char * path)373 test_open_close(uintmax_t num, uintmax_t int_arg __unused, const char *path)
374 {
375 uintmax_t i;
376 int fd;
377
378 fd = open(path, O_RDONLY);
379 if (fd < 0)
380 err(-1, "test_open_close: %s", path);
381 close(fd);
382
383 benchmark_start();
384 BENCHMARK_FOREACH(i, num) {
385 fd = open(path, O_RDONLY);
386 if (fd < 0)
387 err(-1, "test_open_close: %s", path);
388 close(fd);
389 }
390 benchmark_stop();
391 return (i);
392 }
393
394 static uintmax_t
test_open_read_close(uintmax_t num,uintmax_t int_arg,const char * path)395 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
396 {
397 char buf[int_arg];
398 uintmax_t i;
399 int fd;
400
401 fd = open(path, O_RDONLY);
402 if (fd < 0)
403 err(-1, "test_open_read_close: %s", path);
404 (void)read(fd, buf, int_arg);
405 close(fd);
406
407 benchmark_start();
408 BENCHMARK_FOREACH(i, num) {
409 fd = open(path, O_RDONLY);
410 if (fd < 0)
411 err(-1, "test_open_read_close: %s", path);
412 (void)read(fd, buf, int_arg);
413 close(fd);
414 }
415 benchmark_stop();
416 return (i);
417 }
418
419 static uintmax_t
test_pipe(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)420 test_pipe(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
421 {
422 int fd[2];
423 uintmax_t i;
424
425 /*
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.
430 */
431 if (pipe(fd) < 0)
432 err(-1, "test_pipe: pipe");
433 close(fd[0]);
434 close(fd[1]);
435 benchmark_start();
436 BENCHMARK_FOREACH(i, num) {
437 if (pipe(fd) == -1)
438 err(-1, "test_pipe: pipe");
439 close(fd[0]);
440 close(fd[1]);
441 }
442 benchmark_stop();
443 return (i);
444 }
445
446 static void
readx(int fd,char * buf,size_t size)447 readx(int fd, char *buf, size_t size)
448 {
449 ssize_t ret;
450
451 do {
452 ret = read(fd, buf, size);
453 if (ret == -1)
454 err(1, "read");
455 assert((size_t)ret <= size);
456 size -= ret;
457 buf += ret;
458 } while (size > 0);
459 }
460
461 static void
writex(int fd,const char * buf,size_t size)462 writex(int fd, const char *buf, size_t size)
463 {
464 ssize_t ret;
465
466 do {
467 ret = write(fd, buf, size);
468 if (ret == -1)
469 err(1, "write");
470 assert((size_t)ret <= size);
471 size -= ret;
472 buf += ret;
473 } while (size > 0);
474 }
475
476 static uintmax_t
test_pipeping(uintmax_t num,uintmax_t int_arg,const char * path __unused)477 test_pipeping(uintmax_t num, uintmax_t int_arg, const char *path __unused)
478 {
479 char buf[int_arg];
480 uintmax_t i;
481 pid_t pid;
482 int fd[2], procfd;
483
484 if (pipe(fd) < 0)
485 err(-1, "pipe");
486
487 pid = pdfork(&procfd, 0);
488 if (pid < 0)
489 err(1, "pdfork");
490
491 if (pid == 0) {
492 close(fd[0]);
493
494 for (;;) {
495 readx(fd[1], buf, int_arg);
496 writex(fd[1], buf, int_arg);
497 }
498 }
499
500 close(fd[1]);
501
502 benchmark_start();
503 BENCHMARK_FOREACH(i, num) {
504 writex(fd[0], buf, int_arg);
505 readx(fd[0], buf, int_arg);
506 }
507 benchmark_stop();
508
509 close(procfd);
510 return (i);
511 }
512
513 #ifdef WITH_PTHREAD
514 struct pipepingtd_ctx {
515 int fd;
516 uintmax_t int_arg;
517 };
518
519 static void *
pipepingtd_proc(void * arg)520 pipepingtd_proc(void *arg)
521 {
522 struct pipepingtd_ctx *ctxp;
523 int fd;
524 void *buf;
525 uintmax_t int_arg;
526
527 ctxp = arg;
528 fd = ctxp->fd;
529 int_arg = ctxp->int_arg;
530
531 buf = malloc(int_arg);
532 if (buf == NULL)
533 err(1, "malloc");
534
535 for (;;) {
536 readx(fd, buf, int_arg);
537 writex(fd, buf, int_arg);
538 }
539 }
540
541 static uintmax_t
test_pipepingtd(uintmax_t num,uintmax_t int_arg,const char * path __unused)542 test_pipepingtd(uintmax_t num, uintmax_t int_arg, const char *path __unused)
543 {
544 struct pipepingtd_ctx ctx;
545 char buf[int_arg];
546 pthread_t td;
547 uintmax_t i;
548 int error, fd[2];
549
550 if (pipe(fd) < 0)
551 err(-1, "pipe");
552
553 ctx.fd = fd[1];
554 ctx.int_arg = int_arg;
555
556 error = pthread_create(&td, NULL, pipepingtd_proc, &ctx);
557 if (error != 0)
558 err(1, "pthread_create");
559
560 benchmark_start();
561 BENCHMARK_FOREACH(i, num) {
562 writex(fd[0], buf, int_arg);
563 readx(fd[0], buf, int_arg);
564 }
565 benchmark_stop();
566 pthread_cancel(td);
567
568 return (i);
569 }
570 #endif /* WITH_PTHREAD */
571
572 static uintmax_t
test_read(uintmax_t num,uintmax_t int_arg,const char * path)573 test_read(uintmax_t num, uintmax_t int_arg, const char *path)
574 {
575 char buf[int_arg];
576 uintmax_t i;
577 int fd;
578
579 fd = open(path, O_RDONLY);
580 if (fd < 0)
581 err(-1, "test_open_read: %s", path);
582 (void)pread(fd, buf, int_arg, 0);
583
584 benchmark_start();
585 BENCHMARK_FOREACH(i, num) {
586 (void)pread(fd, buf, int_arg, 0);
587 }
588 benchmark_stop();
589 close(fd);
590 return (i);
591 }
592
593 static uintmax_t
test_select(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)594 test_select(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
595 {
596 fd_set readfds, writefds, exceptfds;
597 struct timeval tv;
598 uintmax_t i;
599
600 FD_ZERO(&readfds);
601 FD_ZERO(&writefds);
602 FD_ZERO(&exceptfds);
603
604 tv.tv_sec = 0;
605 tv.tv_usec = 0;
606
607 benchmark_start();
608 BENCHMARK_FOREACH(i, num) {
609 (void)select(0, &readfds, &writefds, &exceptfds, &tv);
610 }
611 benchmark_stop();
612 return (i);
613 }
614
615 static uintmax_t
test_semaping(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)616 test_semaping(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
617 {
618 uintmax_t i;
619 pid_t pid;
620 sem_t *buf;
621 int error, j, procfd;
622
623 buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
624 if (buf == MAP_FAILED)
625 err(1, "mmap");
626
627 for (j = 0; j < 2; j++) {
628 error = sem_init(&buf[j], 1, 0);
629 if (error != 0)
630 err(1, "sem_init");
631 }
632
633 pid = pdfork(&procfd, 0);
634 if (pid < 0)
635 err(1, "pdfork");
636
637 if (pid == 0) {
638 for (;;) {
639 error = sem_wait(&buf[0]);
640 if (error != 0)
641 err(1, "sem_wait");
642 error = sem_post(&buf[1]);
643 if (error != 0)
644 err(1, "sem_post");
645 }
646 }
647
648 benchmark_start();
649 BENCHMARK_FOREACH(i, num) {
650 error = sem_post(&buf[0]);
651 if (error != 0)
652 err(1, "sem_post");
653 error = sem_wait(&buf[1]);
654 if (error != 0)
655 err(1, "sem_wait");
656 }
657 benchmark_stop();
658
659 close(procfd);
660
661 for (j = 0; j < 2; j++) {
662 error = sem_destroy(&buf[j]);
663 if (error != 0)
664 err(1, "sem_destroy");
665 }
666
667 error = munmap(buf, PAGE_SIZE);
668 if (error != 0)
669 err(1, "munmap");
670
671 return (i);
672 }
673
674 static uintmax_t
test_setuid(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)675 test_setuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
676 {
677 uid_t uid;
678 uintmax_t i;
679
680 uid = getuid();
681 if (setuid(uid) < 0)
682 err(-1, "test_setuid: setuid");
683 benchmark_start();
684 BENCHMARK_FOREACH(i, num) {
685 if (setuid(uid) < 0)
686 err(-1, "test_setuid: setuid");
687 }
688 benchmark_stop();
689 return (i);
690 }
691
692 static uintmax_t
test_shmfd(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)693 test_shmfd(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
694 {
695 uintmax_t i;
696 int shmfd;
697
698 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
699 if (shmfd < 0)
700 err(-1, "test_shmfd: shm_open");
701 close(shmfd);
702 benchmark_start();
703 BENCHMARK_FOREACH(i, num) {
704 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
705 if (shmfd < 0)
706 err(-1, "test_shmfd: shm_open");
707 close(shmfd);
708 }
709 benchmark_stop();
710 return (i);
711 }
712
713 static uintmax_t
test_shmfd_dup(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)714 test_shmfd_dup(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
715 {
716 uintmax_t i;
717 int fd, shmfd;
718
719 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
720 if (shmfd < 0)
721 err(-1, "test_shmfd_dup: shm_open");
722 fd = dup(shmfd);
723 if (fd >= 0)
724 close(fd);
725 benchmark_start();
726 BENCHMARK_FOREACH(i, num) {
727 fd = dup(shmfd);
728 if (fd >= 0)
729 close(fd);
730 }
731 benchmark_stop();
732 close(shmfd);
733 return (i);
734 }
735
736 static uintmax_t
test_shmfd_fstat(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)737 test_shmfd_fstat(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
738 {
739 struct stat sb;
740 uintmax_t i;
741 int shmfd;
742
743 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
744 if (shmfd < 0)
745 err(-1, "test_shmfd_fstat: shm_open");
746 if (fstat(shmfd, &sb) < 0)
747 err(-1, "test_shmfd_fstat: fstat");
748 benchmark_start();
749 BENCHMARK_FOREACH(i, num) {
750 (void)fstat(shmfd, &sb);
751 }
752 benchmark_stop();
753 close(shmfd);
754 return (i);
755 }
756
757 static uintmax_t
test_socket_stream(uintmax_t num,uintmax_t int_arg,const char * path __unused)758 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path __unused)
759 {
760 uintmax_t i;
761 int so;
762
763 so = socket(int_arg, SOCK_STREAM, 0);
764 if (so < 0)
765 err(-1, "test_socket_stream: socket");
766 close(so);
767 benchmark_start();
768 BENCHMARK_FOREACH(i, num) {
769 so = socket(int_arg, SOCK_STREAM, 0);
770 if (so == -1)
771 err(-1, "test_socket_stream: socket");
772 close(so);
773 }
774 benchmark_stop();
775 return (i);
776 }
777
778 static uintmax_t
test_socket_dgram(uintmax_t num,uintmax_t int_arg,const char * path __unused)779 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path __unused)
780 {
781 uintmax_t i;
782 int so;
783
784 so = socket(int_arg, SOCK_DGRAM, 0);
785 if (so < 0)
786 err(-1, "test_socket_dgram: socket");
787 close(so);
788 benchmark_start();
789 BENCHMARK_FOREACH(i, num) {
790 so = socket(int_arg, SOCK_DGRAM, 0);
791 if (so == -1)
792 err(-1, "test_socket_dgram: socket");
793 close(so);
794 }
795 benchmark_stop();
796 return (i);
797 }
798
799 static uintmax_t
test_socketpair_stream(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)800 test_socketpair_stream(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
801 {
802 uintmax_t i;
803 int so[2];
804
805 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
806 err(-1, "test_socketpair_stream: socketpair");
807 close(so[0]);
808 close(so[1]);
809 benchmark_start();
810 BENCHMARK_FOREACH(i, num) {
811 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
812 err(-1, "test_socketpair_stream: socketpair");
813 close(so[0]);
814 close(so[1]);
815 }
816 benchmark_stop();
817 return (i);
818 }
819
820 static uintmax_t
test_socketpair_dgram(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)821 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
822 {
823 uintmax_t i;
824 int so[2];
825
826 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
827 err(-1, "test_socketpair_dgram: socketpair");
828 close(so[0]);
829 close(so[1]);
830 benchmark_start();
831 BENCHMARK_FOREACH(i, num) {
832 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
833 err(-1, "test_socketpair_dgram: socketpair");
834 close(so[0]);
835 close(so[1]);
836 }
837 benchmark_stop();
838 return (i);
839 }
840
841 static uintmax_t
test_readlink(uintmax_t num,uintmax_t int_arg __unused,const char * path)842 test_readlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
843 {
844 char buf[PATH_MAX];
845 ssize_t rv;
846 uintmax_t i;
847
848 benchmark_start();
849 BENCHMARK_FOREACH(i, num) {
850 rv = readlink(path, buf, sizeof(buf));
851 if (rv < 0 && errno != EINVAL)
852 err(-1, "readlink");
853 }
854 benchmark_stop();
855 return (i);
856 }
857
858 static uintmax_t
test_vfork(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)859 test_vfork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
860 {
861 pid_t pid;
862 uintmax_t i;
863
864 pid = vfork();
865 if (pid < 0)
866 err(-1, "test_vfork: vfork");
867 if (pid == 0)
868 _exit(0);
869 if (waitpid(pid, NULL, 0) < 0)
870 err(-1, "test_vfork: waitpid");
871 benchmark_start();
872 BENCHMARK_FOREACH(i, num) {
873 pid = vfork();
874 if (pid < 0)
875 err(-1, "test_vfork: vfork");
876 if (pid == 0)
877 _exit(0);
878 if (waitpid(pid, NULL, 0) < 0)
879 err(-1, "test_vfork: waitpid");
880 }
881 benchmark_stop();
882 return (i);
883 }
884
885 static uintmax_t
test_vfork_exec(uintmax_t num,uintmax_t int_arg __unused,const char * path __unused)886 test_vfork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
887 {
888 pid_t pid;
889 uintmax_t i;
890
891 pid = vfork();
892 if (pid < 0)
893 err(-1, "test_vfork_exec: vfork");
894 if (pid == 0) {
895 (void)execve(USR_BIN_TRUE, execve_args, environ);
896 err(-1, "test_vfork_exec: execve");
897 }
898 if (waitpid(pid, NULL, 0) < 0)
899 err(-1, "test_vfork_exec: waitpid");
900 benchmark_start();
901 BENCHMARK_FOREACH(i, num) {
902 pid = vfork();
903 if (pid < 0)
904 err(-1, "test_vfork_exec: vfork");
905 if (pid == 0) {
906 (void)execve(USR_BIN_TRUE, execve_args, environ);
907 err(-1, "execve");
908 }
909 if (waitpid(pid, NULL, 0) < 0)
910 err(-1, "test_vfork_exec: waitpid");
911 }
912 benchmark_stop();
913 return (i);
914 }
915
916 struct test {
917 const char *t_name;
918 uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *);
919 int t_flags;
920 uintmax_t t_int;
921 };
922
923 #define FLAG_PATH 0x00000001
924
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,
949 .t_int = 1 },
950 { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
951 .t_int = 10 },
952 { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
953 .t_int = 100 },
954 { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
955 .t_int = 1000 },
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 },
970 #ifdef WITH_PTHREAD
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 },
978 #endif
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 },
1001 };
1002 static const int tests_count = sizeof(tests) / sizeof(tests[0]);
1003
1004 static void
usage(void)1005 usage(void)
1006 {
1007 int i;
1008
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);
1013 exit(-1);
1014 }
1015
1016 int
main(int argc,char * argv[])1017 main(int argc, char *argv[])
1018 {
1019 struct timespec ts_res;
1020 const struct test *the_test;
1021 const char *path;
1022 char *tmp_dir, *tmp_path;
1023 long long ll;
1024 char *endp;
1025 int ch, fd, error, i, j, rv;
1026 uintmax_t iterations, k, loops;
1027
1028 alarm_timeout = 1;
1029 iterations = 0;
1030 loops = 10;
1031 path = NULL;
1032 tmp_path = NULL;
1033 while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
1034 switch (ch) {
1035 case 'i':
1036 ll = strtol(optarg, &endp, 10);
1037 if (*endp != 0 || ll < 1)
1038 usage();
1039 iterations = ll;
1040 break;
1041
1042 case 'l':
1043 ll = strtol(optarg, &endp, 10);
1044 if (*endp != 0 || ll < 1 || ll > 100000)
1045 usage();
1046 loops = ll;
1047 break;
1048
1049 case 'p':
1050 path = optarg;
1051 break;
1052
1053 case 's':
1054 ll = strtol(optarg, &endp, 10);
1055 if (*endp != 0 || ll < 1 || ll > 60*60)
1056 usage();
1057 alarm_timeout = ll;
1058 break;
1059
1060 case '?':
1061 default:
1062 usage();
1063 }
1064 }
1065 argc -= optind;
1066 argv += optind;
1067
1068 if (iterations < 1 && alarm_timeout < 1)
1069 usage();
1070 if (iterations < 1)
1071 iterations = UINT64_MAX;
1072 if (loops < 1)
1073 loops = 1;
1074
1075 if (argc < 1)
1076 usage();
1077
1078 /*
1079 * Validate test list and that, if a path is required, it is
1080 * defined.
1081 */
1082 for (j = 0; j < argc; j++) {
1083 the_test = NULL;
1084 for (i = 0; i < tests_count; i++) {
1085 if (strcmp(argv[j], tests[i].t_name) == 0)
1086 the_test = &tests[i];
1087 }
1088 if (the_test == NULL)
1089 usage();
1090 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
1091 tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
1092 if (tmp_dir == NULL)
1093 err(1, "strdup");
1094 tmp_dir = mkdtemp(tmp_dir);
1095 if (tmp_dir == NULL)
1096 err(1, "mkdtemp");
1097 rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
1098 if (rv <= 0)
1099 err(1, "asprintf");
1100 }
1101 }
1102
1103 error = clock_getres(CLOCK_REALTIME, &ts_res);
1104 assert(error == 0);
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");
1108
1109 for (j = 0; j < argc; j++) {
1110 uintmax_t calls, nsecsperit;
1111
1112 the_test = NULL;
1113 for (i = 0; i < tests_count; i++) {
1114 if (strcmp(argv[j], tests[i].t_name) == 0)
1115 the_test = &tests[i];
1116 }
1117
1118 if (tmp_path != NULL) {
1119 fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
1120 if (fd < 0)
1121 err(1, "cannot open %s", tmp_path);
1122 error = ftruncate(fd, 1000000);
1123 if (error != 0)
1124 err(1, "ftruncate");
1125 error = close(fd);
1126 if (error != 0)
1127 err(1, "close");
1128 path = tmp_path;
1129 }
1130
1131 /*
1132 * Run one warmup, then do the real thing (loops) times.
1133 */
1134 the_test->t_func(iterations, the_test->t_int, path);
1135 calls = 0;
1136 for (k = 0; k < loops; k++) {
1137 calls = the_test->t_func(iterations, the_test->t_int,
1138 path);
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);
1143
1144 /*
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.
1149 */
1150 nsecsperit = ts_end.tv_sec * 1000000000;
1151 nsecsperit += ts_end.tv_nsec;
1152 nsecsperit /= calls;
1153 printf("0.%09ju\n", (uintmax_t)nsecsperit);
1154 }
1155 }
1156
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);
1162 if (error != 0)
1163 warn("cannot rmdir %s", tmp_dir);
1164 }
1165
1166 return (0);
1167 }
1168