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 58static struct timespec ts_start, ts_end; 59static int alarm_timeout; 60static volatile int alarm_fired; 61 62#define BENCHMARK_FOREACH(I, NUM) for (I = 0; I < NUM && alarm_fired == 0; I++) 63 64static void 65alarm_handler(int signum __unused) 66{ 67 68 alarm_fired = 1; 69} 70 71static void 72benchmark_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 85static void 86benchmark_stop(void) 87{ 88 int error; 89 90 error = clock_gettime(CLOCK_REALTIME, &ts_end); 91 assert(error == 0); 92} 93 94static uintmax_t 95test_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 114static uintmax_t 115test_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 127static uintmax_t 128test_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 143static uintmax_t 144test_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 157static uintmax_t 158test_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 183static uintmax_t 184test_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" 211static char *execve_args[] = { __DECONST(char *, USR_BIN_TRUE), NULL}; 212extern char **environ; 213 214static uintmax_t 215test_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 245static uintmax_t 246test_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 262static uintmax_t 263test_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 */ 279static uintmax_t 280test_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 292static uintmax_t 293test_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 306static uintmax_t 307test_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 320static uintmax_t 321test_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 337static uintmax_t 338test_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 357static uintmax_t 358test_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 379static uintmax_t 380test_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 404static uintmax_t 405test_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 431static void 432readx(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 446static void 447writex(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 461static uintmax_t 462test_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 499struct pipepingtd_ctx { 500 int fd; 501 uintmax_t int_arg; 502}; 503 504static void * 505pipepingtd_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 526static uintmax_t 527test_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 557static uintmax_t 558test_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 578static uintmax_t 579test_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 600static uintmax_t 601test_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 659static uintmax_t 660test_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 677static uintmax_t 678test_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 698static uintmax_t 699test_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 721static uintmax_t 722test_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 742static uintmax_t 743test_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 763static uintmax_t 764test_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 784static uintmax_t 785test_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 805static uintmax_t 806test_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 826static uintmax_t 827test_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 853static uintmax_t 854test_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 884struct 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 893static 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}; 968static const int tests_count = sizeof(tests) / sizeof(tests[0]); 969 970static void 971usage(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 982int 983main(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