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 65 alarm_handler(int signum __unused) 66 { 67 68 alarm_fired = 1; 69 } 70 71 static 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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