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