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_lstat(uintmax_t num, uintmax_t int_arg __unused, const char *path) 339 { 340 struct stat sb; 341 uintmax_t i; 342 int error; 343 344 benchmark_start(); 345 BENCHMARK_FOREACH(i, num) { 346 error = lstat(path, &sb); 347 if (error != 0) 348 err(-1, "lstat"); 349 } 350 benchmark_stop(); 351 return (i); 352 } 353 354 static uintmax_t 355 test_memcpy(uintmax_t num, uintmax_t int_arg, const char *path __unused) 356 { 357 char buf[int_arg], buf2[int_arg]; 358 uintmax_t i; 359 360 benchmark_start(); 361 BENCHMARK_FOREACH(i, num) { 362 /* 363 * Copy the memory there and back, to match the total amount 364 * moved by pipeping/pipepingtd tests. 365 */ 366 memcpy(buf2, buf, int_arg); 367 memcpy(buf, buf2, int_arg); 368 } 369 benchmark_stop(); 370 371 return (i); 372 } 373 374 static uintmax_t 375 test_open_close(uintmax_t num, uintmax_t int_arg __unused, const char *path) 376 { 377 uintmax_t i; 378 int fd; 379 380 fd = open(path, O_RDONLY); 381 if (fd < 0) 382 err(-1, "test_open_close: %s", path); 383 close(fd); 384 385 benchmark_start(); 386 BENCHMARK_FOREACH(i, num) { 387 fd = open(path, O_RDONLY); 388 if (fd < 0) 389 err(-1, "test_open_close: %s", path); 390 close(fd); 391 } 392 benchmark_stop(); 393 return (i); 394 } 395 396 static uintmax_t 397 test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path) 398 { 399 char buf[int_arg]; 400 uintmax_t i; 401 int fd; 402 403 fd = open(path, O_RDONLY); 404 if (fd < 0) 405 err(-1, "test_open_read_close: %s", path); 406 (void)read(fd, buf, int_arg); 407 close(fd); 408 409 benchmark_start(); 410 BENCHMARK_FOREACH(i, num) { 411 fd = open(path, O_RDONLY); 412 if (fd < 0) 413 err(-1, "test_open_read_close: %s", path); 414 (void)read(fd, buf, int_arg); 415 close(fd); 416 } 417 benchmark_stop(); 418 return (i); 419 } 420 421 static uintmax_t 422 test_pipe(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 423 { 424 int fd[2]; 425 uintmax_t i; 426 427 /* 428 * pipe creation is expensive, as it will allocate a new file 429 * descriptor, allocate a new pipe, hook it all up, and return. 430 * Destroying is also expensive, as we now have to free up 431 * the file descriptors and return the pipe. 432 */ 433 if (pipe(fd) < 0) 434 err(-1, "test_pipe: pipe"); 435 close(fd[0]); 436 close(fd[1]); 437 benchmark_start(); 438 BENCHMARK_FOREACH(i, num) { 439 if (pipe(fd) == -1) 440 err(-1, "test_pipe: pipe"); 441 close(fd[0]); 442 close(fd[1]); 443 } 444 benchmark_stop(); 445 return (i); 446 } 447 448 static void 449 readx(int fd, char *buf, size_t size) 450 { 451 ssize_t ret; 452 453 do { 454 ret = read(fd, buf, size); 455 if (ret == -1) 456 err(1, "read"); 457 assert((size_t)ret <= size); 458 size -= ret; 459 buf += ret; 460 } while (size > 0); 461 } 462 463 static void 464 writex(int fd, const char *buf, size_t size) 465 { 466 ssize_t ret; 467 468 do { 469 ret = write(fd, buf, size); 470 if (ret == -1) 471 err(1, "write"); 472 assert((size_t)ret <= size); 473 size -= ret; 474 buf += ret; 475 } while (size > 0); 476 } 477 478 static uintmax_t 479 test_pipeping(uintmax_t num, uintmax_t int_arg, const char *path __unused) 480 { 481 char buf[int_arg]; 482 uintmax_t i; 483 pid_t pid; 484 int fd[2], procfd; 485 486 if (pipe(fd) < 0) 487 err(-1, "pipe"); 488 489 pid = pdfork(&procfd, 0); 490 if (pid < 0) 491 err(1, "pdfork"); 492 493 if (pid == 0) { 494 close(fd[0]); 495 496 for (;;) { 497 readx(fd[1], buf, int_arg); 498 writex(fd[1], buf, int_arg); 499 } 500 } 501 502 close(fd[1]); 503 504 benchmark_start(); 505 BENCHMARK_FOREACH(i, num) { 506 writex(fd[0], buf, int_arg); 507 readx(fd[0], buf, int_arg); 508 } 509 benchmark_stop(); 510 511 close(procfd); 512 return (i); 513 } 514 515 #ifdef WITH_PTHREAD 516 struct pipepingtd_ctx { 517 int fd; 518 uintmax_t int_arg; 519 }; 520 521 static void * 522 pipepingtd_proc(void *arg) 523 { 524 struct pipepingtd_ctx *ctxp; 525 int fd; 526 void *buf; 527 uintmax_t int_arg; 528 529 ctxp = arg; 530 fd = ctxp->fd; 531 int_arg = ctxp->int_arg; 532 533 buf = malloc(int_arg); 534 if (buf == NULL) 535 err(1, "malloc"); 536 537 for (;;) { 538 readx(fd, buf, int_arg); 539 writex(fd, buf, int_arg); 540 } 541 } 542 543 static uintmax_t 544 test_pipepingtd(uintmax_t num, uintmax_t int_arg, const char *path __unused) 545 { 546 struct pipepingtd_ctx ctx; 547 char buf[int_arg]; 548 pthread_t td; 549 uintmax_t i; 550 int error, fd[2]; 551 552 if (pipe(fd) < 0) 553 err(-1, "pipe"); 554 555 ctx.fd = fd[1]; 556 ctx.int_arg = int_arg; 557 558 error = pthread_create(&td, NULL, pipepingtd_proc, &ctx); 559 if (error != 0) 560 err(1, "pthread_create"); 561 562 benchmark_start(); 563 BENCHMARK_FOREACH(i, num) { 564 writex(fd[0], buf, int_arg); 565 readx(fd[0], buf, int_arg); 566 } 567 benchmark_stop(); 568 pthread_cancel(td); 569 570 return (i); 571 } 572 #endif /* WITH_PTHREAD */ 573 574 static uintmax_t 575 test_read(uintmax_t num, uintmax_t int_arg, const char *path) 576 { 577 char buf[int_arg]; 578 uintmax_t i; 579 int fd; 580 581 fd = open(path, O_RDONLY); 582 if (fd < 0) 583 err(-1, "test_open_read: %s", path); 584 (void)pread(fd, buf, int_arg, 0); 585 586 benchmark_start(); 587 BENCHMARK_FOREACH(i, num) { 588 (void)pread(fd, buf, int_arg, 0); 589 } 590 benchmark_stop(); 591 close(fd); 592 return (i); 593 } 594 595 static uintmax_t 596 test_select(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 597 { 598 fd_set readfds, writefds, exceptfds; 599 struct timeval tv; 600 uintmax_t i; 601 602 FD_ZERO(&readfds); 603 FD_ZERO(&writefds); 604 FD_ZERO(&exceptfds); 605 606 tv.tv_sec = 0; 607 tv.tv_usec = 0; 608 609 benchmark_start(); 610 BENCHMARK_FOREACH(i, num) { 611 (void)select(0, &readfds, &writefds, &exceptfds, &tv); 612 } 613 benchmark_stop(); 614 return (i); 615 } 616 617 static uintmax_t 618 test_semaping(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 619 { 620 uintmax_t i; 621 pid_t pid; 622 sem_t *buf; 623 int error, j, procfd; 624 625 buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); 626 if (buf == MAP_FAILED) 627 err(1, "mmap"); 628 629 for (j = 0; j < 2; j++) { 630 error = sem_init(&buf[j], 1, 0); 631 if (error != 0) 632 err(1, "sem_init"); 633 } 634 635 pid = pdfork(&procfd, 0); 636 if (pid < 0) 637 err(1, "pdfork"); 638 639 if (pid == 0) { 640 for (;;) { 641 error = sem_wait(&buf[0]); 642 if (error != 0) 643 err(1, "sem_wait"); 644 error = sem_post(&buf[1]); 645 if (error != 0) 646 err(1, "sem_post"); 647 } 648 } 649 650 benchmark_start(); 651 BENCHMARK_FOREACH(i, num) { 652 error = sem_post(&buf[0]); 653 if (error != 0) 654 err(1, "sem_post"); 655 error = sem_wait(&buf[1]); 656 if (error != 0) 657 err(1, "sem_wait"); 658 } 659 benchmark_stop(); 660 661 close(procfd); 662 663 for (j = 0; j < 2; j++) { 664 error = sem_destroy(&buf[j]); 665 if (error != 0) 666 err(1, "sem_destroy"); 667 } 668 669 error = munmap(buf, PAGE_SIZE); 670 if (error != 0) 671 err(1, "munmap"); 672 673 return (i); 674 } 675 676 static uintmax_t 677 test_setuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 678 { 679 uid_t uid; 680 uintmax_t i; 681 682 uid = getuid(); 683 if (setuid(uid) < 0) 684 err(-1, "test_setuid: setuid"); 685 benchmark_start(); 686 BENCHMARK_FOREACH(i, num) { 687 if (setuid(uid) < 0) 688 err(-1, "test_setuid: setuid"); 689 } 690 benchmark_stop(); 691 return (i); 692 } 693 694 static uintmax_t 695 test_shmfd(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 696 { 697 uintmax_t i; 698 int shmfd; 699 700 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 701 if (shmfd < 0) 702 err(-1, "test_shmfd: shm_open"); 703 close(shmfd); 704 benchmark_start(); 705 BENCHMARK_FOREACH(i, num) { 706 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 707 if (shmfd < 0) 708 err(-1, "test_shmfd: shm_open"); 709 close(shmfd); 710 } 711 benchmark_stop(); 712 return (i); 713 } 714 715 static uintmax_t 716 test_shmfd_dup(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 717 { 718 uintmax_t i; 719 int fd, shmfd; 720 721 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 722 if (shmfd < 0) 723 err(-1, "test_shmfd_dup: shm_open"); 724 fd = dup(shmfd); 725 if (fd >= 0) 726 close(fd); 727 benchmark_start(); 728 BENCHMARK_FOREACH(i, num) { 729 fd = dup(shmfd); 730 if (fd >= 0) 731 close(fd); 732 } 733 benchmark_stop(); 734 close(shmfd); 735 return (i); 736 } 737 738 static uintmax_t 739 test_shmfd_fstat(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 740 { 741 struct stat sb; 742 uintmax_t i; 743 int shmfd; 744 745 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 746 if (shmfd < 0) 747 err(-1, "test_shmfd_fstat: shm_open"); 748 if (fstat(shmfd, &sb) < 0) 749 err(-1, "test_shmfd_fstat: fstat"); 750 benchmark_start(); 751 BENCHMARK_FOREACH(i, num) { 752 (void)fstat(shmfd, &sb); 753 } 754 benchmark_stop(); 755 close(shmfd); 756 return (i); 757 } 758 759 static uintmax_t 760 test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path __unused) 761 { 762 uintmax_t i; 763 int so; 764 765 so = socket(int_arg, SOCK_STREAM, 0); 766 if (so < 0) 767 err(-1, "test_socket_stream: socket"); 768 close(so); 769 benchmark_start(); 770 BENCHMARK_FOREACH(i, num) { 771 so = socket(int_arg, SOCK_STREAM, 0); 772 if (so == -1) 773 err(-1, "test_socket_stream: socket"); 774 close(so); 775 } 776 benchmark_stop(); 777 return (i); 778 } 779 780 static uintmax_t 781 test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path __unused) 782 { 783 uintmax_t i; 784 int so; 785 786 so = socket(int_arg, SOCK_DGRAM, 0); 787 if (so < 0) 788 err(-1, "test_socket_dgram: socket"); 789 close(so); 790 benchmark_start(); 791 BENCHMARK_FOREACH(i, num) { 792 so = socket(int_arg, SOCK_DGRAM, 0); 793 if (so == -1) 794 err(-1, "test_socket_dgram: socket"); 795 close(so); 796 } 797 benchmark_stop(); 798 return (i); 799 } 800 801 static uintmax_t 802 test_socketpair_stream(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 803 { 804 uintmax_t i; 805 int so[2]; 806 807 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1) 808 err(-1, "test_socketpair_stream: socketpair"); 809 close(so[0]); 810 close(so[1]); 811 benchmark_start(); 812 BENCHMARK_FOREACH(i, num) { 813 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1) 814 err(-1, "test_socketpair_stream: socketpair"); 815 close(so[0]); 816 close(so[1]); 817 } 818 benchmark_stop(); 819 return (i); 820 } 821 822 static uintmax_t 823 test_socketpair_dgram(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 824 { 825 uintmax_t i; 826 int so[2]; 827 828 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1) 829 err(-1, "test_socketpair_dgram: socketpair"); 830 close(so[0]); 831 close(so[1]); 832 benchmark_start(); 833 BENCHMARK_FOREACH(i, num) { 834 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1) 835 err(-1, "test_socketpair_dgram: socketpair"); 836 close(so[0]); 837 close(so[1]); 838 } 839 benchmark_stop(); 840 return (i); 841 } 842 843 static uintmax_t 844 test_readlink(uintmax_t num, uintmax_t int_arg __unused, const char *path) 845 { 846 char buf[PATH_MAX]; 847 ssize_t rv; 848 uintmax_t i; 849 850 benchmark_start(); 851 BENCHMARK_FOREACH(i, num) { 852 rv = readlink(path, buf, sizeof(buf)); 853 if (rv < 0 && errno != EINVAL) 854 err(-1, "readlink"); 855 } 856 benchmark_stop(); 857 return (i); 858 } 859 860 static uintmax_t 861 test_vfork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 862 { 863 pid_t pid; 864 uintmax_t i; 865 866 pid = vfork(); 867 if (pid < 0) 868 err(-1, "test_vfork: vfork"); 869 if (pid == 0) 870 _exit(0); 871 if (waitpid(pid, NULL, 0) < 0) 872 err(-1, "test_vfork: waitpid"); 873 benchmark_start(); 874 BENCHMARK_FOREACH(i, num) { 875 pid = vfork(); 876 if (pid < 0) 877 err(-1, "test_vfork: vfork"); 878 if (pid == 0) 879 _exit(0); 880 if (waitpid(pid, NULL, 0) < 0) 881 err(-1, "test_vfork: waitpid"); 882 } 883 benchmark_stop(); 884 return (i); 885 } 886 887 static uintmax_t 888 test_vfork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused) 889 { 890 pid_t pid; 891 uintmax_t i; 892 893 pid = vfork(); 894 if (pid < 0) 895 err(-1, "test_vfork_exec: vfork"); 896 if (pid == 0) { 897 (void)execve(USR_BIN_TRUE, execve_args, environ); 898 err(-1, "test_vfork_exec: execve"); 899 } 900 if (waitpid(pid, NULL, 0) < 0) 901 err(-1, "test_vfork_exec: waitpid"); 902 benchmark_start(); 903 BENCHMARK_FOREACH(i, num) { 904 pid = vfork(); 905 if (pid < 0) 906 err(-1, "test_vfork_exec: vfork"); 907 if (pid == 0) { 908 (void)execve(USR_BIN_TRUE, execve_args, environ); 909 err(-1, "execve"); 910 } 911 if (waitpid(pid, NULL, 0) < 0) 912 err(-1, "test_vfork_exec: waitpid"); 913 } 914 benchmark_stop(); 915 return (i); 916 } 917 918 struct test { 919 const char *t_name; 920 uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *); 921 int t_flags; 922 uintmax_t t_int; 923 }; 924 925 #define FLAG_PATH 0x00000001 926 927 static const struct test tests[] = { 928 { "access", test_access, .t_flags = FLAG_PATH }, 929 { "bad_open", test_bad_open, .t_flags = 0 }, 930 { "chroot", test_chroot, .t_flags = 0 }, 931 { "clock_gettime", test_clock_gettime, .t_flags = 0 }, 932 { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH }, 933 { "fork", test_fork, .t_flags = 0 }, 934 { "fork_exec", test_fork_exec, .t_flags = 0 }, 935 { "getppid", test_getppid, .t_flags = 0 }, 936 { "getpriority", test_getpriority, .t_flags = 0 }, 937 { "getprogname", test_getprogname, .t_flags = 0 }, 938 { "getresuid", test_getresuid, .t_flags = 0 }, 939 { "gettimeofday", test_gettimeofday, .t_flags = 0 }, 940 { "getuid", test_getuid, .t_flags = 0 }, 941 { "lstat", test_lstat, .t_flags = FLAG_PATH }, 942 { "memcpy_1", test_memcpy, .t_flags = 0, .t_int = 1 }, 943 { "memcpy_10", test_memcpy, .t_flags = 0, .t_int = 10 }, 944 { "memcpy_100", test_memcpy, .t_flags = 0, .t_int = 100 }, 945 { "memcpy_1000", test_memcpy, .t_flags = 0, .t_int = 1000 }, 946 { "memcpy_10000", test_memcpy, .t_flags = 0, .t_int = 10000 }, 947 { "memcpy_100000", test_memcpy, .t_flags = 0, .t_int = 100000 }, 948 { "memcpy_1000000", test_memcpy, .t_flags = 0, .t_int = 1000000 }, 949 { "open_close", test_open_close, .t_flags = FLAG_PATH }, 950 { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH, 951 .t_int = 1 }, 952 { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH, 953 .t_int = 10 }, 954 { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH, 955 .t_int = 100 }, 956 { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH, 957 .t_int = 1000 }, 958 { "open_read_close_10000", test_open_read_close, 959 .t_flags = FLAG_PATH, .t_int = 10000 }, 960 { "open_read_close_100000", test_open_read_close, 961 .t_flags = FLAG_PATH, .t_int = 100000 }, 962 { "open_read_close_1000000", test_open_read_close, 963 .t_flags = FLAG_PATH, .t_int = 1000000 }, 964 { "pipe", test_pipe, .t_flags = 0 }, 965 { "pipeping_1", test_pipeping, .t_flags = 0, .t_int = 1 }, 966 { "pipeping_10", test_pipeping, .t_flags = 0, .t_int = 10 }, 967 { "pipeping_100", test_pipeping, .t_flags = 0, .t_int = 100 }, 968 { "pipeping_1000", test_pipeping, .t_flags = 0, .t_int = 1000 }, 969 { "pipeping_10000", test_pipeping, .t_flags = 0, .t_int = 10000 }, 970 { "pipeping_100000", test_pipeping, .t_flags = 0, .t_int = 100000 }, 971 { "pipeping_1000000", test_pipeping, .t_flags = 0, .t_int = 1000000 }, 972 #ifdef WITH_PTHREAD 973 { "pipepingtd_1", test_pipepingtd, .t_flags = 0, .t_int = 1 }, 974 { "pipepingtd_10", test_pipepingtd, .t_flags = 0, .t_int = 10 }, 975 { "pipepingtd_100", test_pipepingtd, .t_flags = 0, .t_int = 100 }, 976 { "pipepingtd_1000", test_pipepingtd, .t_flags = 0, .t_int = 1000 }, 977 { "pipepingtd_10000", test_pipepingtd, .t_flags = 0, .t_int = 10000 }, 978 { "pipepingtd_100000", test_pipepingtd, .t_flags = 0, .t_int = 100000 }, 979 { "pipepingtd_1000000", test_pipepingtd, .t_flags = 0, .t_int = 1000000 }, 980 #endif 981 { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 }, 982 { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 }, 983 { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 }, 984 { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 }, 985 { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 }, 986 { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 }, 987 { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 }, 988 { "select", test_select, .t_flags = 0 }, 989 { "semaping", test_semaping, .t_flags = 0 }, 990 { "setuid", test_setuid, .t_flags = 0 }, 991 { "shmfd", test_shmfd, .t_flags = 0 }, 992 { "shmfd_dup", test_shmfd_dup, .t_flags = 0 }, 993 { "shmfd_fstat", test_shmfd_fstat, .t_flags = 0 }, 994 { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL }, 995 { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL }, 996 { "socketpair_stream", test_socketpair_stream, .t_flags = 0 }, 997 { "socketpair_dgram", test_socketpair_dgram, .t_flags = 0 }, 998 { "socket_tcp", test_socket_stream, .t_int = PF_INET }, 999 { "socket_udp", test_socket_dgram, .t_int = PF_INET }, 1000 { "readlink", test_readlink, .t_flags = FLAG_PATH }, 1001 { "vfork", test_vfork, .t_flags = 0 }, 1002 { "vfork_exec", test_vfork_exec, .t_flags = 0 }, 1003 }; 1004 static const int tests_count = sizeof(tests) / sizeof(tests[0]); 1005 1006 static void 1007 usage(void) 1008 { 1009 int i; 1010 1011 fprintf(stderr, "syscall_timing [-i iterations] [-l loops] " 1012 "[-p path] [-s seconds] test\n"); 1013 for (i = 0; i < tests_count; i++) 1014 fprintf(stderr, " %s\n", tests[i].t_name); 1015 exit(-1); 1016 } 1017 1018 int 1019 main(int argc, char *argv[]) 1020 { 1021 struct timespec ts_res; 1022 const struct test *the_test; 1023 const char *path; 1024 char *tmp_dir, *tmp_path; 1025 long long ll; 1026 char *endp; 1027 int ch, fd, error, i, j, rv; 1028 uintmax_t iterations, k, loops; 1029 1030 alarm_timeout = 1; 1031 iterations = 0; 1032 loops = 10; 1033 path = NULL; 1034 tmp_path = NULL; 1035 while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) { 1036 switch (ch) { 1037 case 'i': 1038 ll = strtol(optarg, &endp, 10); 1039 if (*endp != 0 || ll < 1) 1040 usage(); 1041 iterations = ll; 1042 break; 1043 1044 case 'l': 1045 ll = strtol(optarg, &endp, 10); 1046 if (*endp != 0 || ll < 1 || ll > 100000) 1047 usage(); 1048 loops = ll; 1049 break; 1050 1051 case 'p': 1052 path = optarg; 1053 break; 1054 1055 case 's': 1056 ll = strtol(optarg, &endp, 10); 1057 if (*endp != 0 || ll < 1 || ll > 60*60) 1058 usage(); 1059 alarm_timeout = ll; 1060 break; 1061 1062 case '?': 1063 default: 1064 usage(); 1065 } 1066 } 1067 argc -= optind; 1068 argv += optind; 1069 1070 if (iterations < 1 && alarm_timeout < 1) 1071 usage(); 1072 if (iterations < 1) 1073 iterations = UINT64_MAX; 1074 if (loops < 1) 1075 loops = 1; 1076 1077 if (argc < 1) 1078 usage(); 1079 1080 /* 1081 * Validate test list and that, if a path is required, it is 1082 * defined. 1083 */ 1084 for (j = 0; j < argc; j++) { 1085 the_test = NULL; 1086 for (i = 0; i < tests_count; i++) { 1087 if (strcmp(argv[j], tests[i].t_name) == 0) 1088 the_test = &tests[i]; 1089 } 1090 if (the_test == NULL) 1091 usage(); 1092 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) { 1093 tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX"); 1094 if (tmp_dir == NULL) 1095 err(1, "strdup"); 1096 tmp_dir = mkdtemp(tmp_dir); 1097 if (tmp_dir == NULL) 1098 err(1, "mkdtemp"); 1099 rv = asprintf(&tmp_path, "%s/testfile", tmp_dir); 1100 if (rv <= 0) 1101 err(1, "asprintf"); 1102 } 1103 } 1104 1105 error = clock_getres(CLOCK_REALTIME, &ts_res); 1106 assert(error == 0); 1107 printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec, 1108 (uintmax_t)ts_res.tv_nsec); 1109 printf("test\tloop\ttime\titerations\tperiteration\n"); 1110 1111 for (j = 0; j < argc; j++) { 1112 uintmax_t calls, nsecsperit; 1113 1114 the_test = NULL; 1115 for (i = 0; i < tests_count; i++) { 1116 if (strcmp(argv[j], tests[i].t_name) == 0) 1117 the_test = &tests[i]; 1118 } 1119 1120 if (tmp_path != NULL) { 1121 fd = open(tmp_path, O_WRONLY | O_CREAT, 0700); 1122 if (fd < 0) 1123 err(1, "cannot open %s", tmp_path); 1124 error = ftruncate(fd, 1000000); 1125 if (error != 0) 1126 err(1, "ftruncate"); 1127 error = close(fd); 1128 if (error != 0) 1129 err(1, "close"); 1130 path = tmp_path; 1131 } 1132 1133 /* 1134 * Run one warmup, then do the real thing (loops) times. 1135 */ 1136 the_test->t_func(iterations, the_test->t_int, path); 1137 calls = 0; 1138 for (k = 0; k < loops; k++) { 1139 calls = the_test->t_func(iterations, the_test->t_int, 1140 path); 1141 timespecsub(&ts_end, &ts_start, &ts_end); 1142 printf("%s\t%ju\t", the_test->t_name, k); 1143 printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec, 1144 (uintmax_t)ts_end.tv_nsec, calls); 1145 1146 /* 1147 * Note. This assumes that each iteration takes less than 1148 * a second, and that our total nanoseconds doesn't exceed 1149 * the room in our arithmetic unit. Fine for system calls, 1150 * but not for long things. 1151 */ 1152 nsecsperit = ts_end.tv_sec * 1000000000; 1153 nsecsperit += ts_end.tv_nsec; 1154 nsecsperit /= calls; 1155 printf("0.%09ju\n", (uintmax_t)nsecsperit); 1156 } 1157 } 1158 1159 if (tmp_path != NULL) { 1160 error = unlink(tmp_path); 1161 if (error != 0 && errno != ENOENT) 1162 warn("cannot unlink %s", tmp_path); 1163 error = rmdir(tmp_dir); 1164 if (error != 0) 1165 warn("cannot rmdir %s", tmp_dir); 1166 } 1167 1168 return (0); 1169 } 1170