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