syscall_timing.c revision 332626
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: stable/11/tools/tools/syscall_timing/syscall_timing.c 332626 2018-04-16 17:28:59Z trasz $ 30 */ 31 32#include <sys/types.h> 33#include <sys/mman.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 <fcntl.h> 43#include <inttypes.h> 44#include <limits.h> 45#include <signal.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <unistd.h> 50 51static struct timespec ts_start, ts_end; 52static int alarm_timeout; 53static volatile int alarm_fired; 54 55#define timespecsub(vvp, uvp) \ 56 do { \ 57 (vvp)->tv_sec -= (uvp)->tv_sec; \ 58 (vvp)->tv_nsec -= (uvp)->tv_nsec; \ 59 if ((vvp)->tv_nsec < 0) { \ 60 (vvp)->tv_sec--; \ 61 (vvp)->tv_nsec += 1000000000; \ 62 } \ 63 } while (0) 64 65static void 66alarm_handler(int signum) 67{ 68 69 alarm_fired = 1; 70} 71 72static void 73benchmark_start(void) 74{ 75 int error; 76 77 alarm_fired = 0; 78 if (alarm_timeout) { 79 signal(SIGALRM, alarm_handler); 80 alarm(alarm_timeout); 81 } 82 error = clock_gettime(CLOCK_REALTIME, &ts_start); 83 assert(error == 0); 84} 85 86static void 87benchmark_stop(void) 88{ 89 int error; 90 91 error = clock_gettime(CLOCK_REALTIME, &ts_end); 92 assert(error == 0); 93} 94 95uintmax_t 96test_getuid(uintmax_t num, uintmax_t int_arg, const char *path) 97{ 98 uintmax_t i; 99 100 /* 101 * Thread-local data should require no locking if system 102 * call is MPSAFE. 103 */ 104 benchmark_start(); 105 for (i = 0; i < num; i++) { 106 if (alarm_fired) 107 break; 108 getuid(); 109 } 110 benchmark_stop(); 111 return (i); 112} 113 114uintmax_t 115test_getppid(uintmax_t num, uintmax_t int_arg, const char *path) 116{ 117 uintmax_t i; 118 119 /* 120 * This is process-local, but can change, so will require a 121 * lock. 122 */ 123 benchmark_start(); 124 for (i = 0; i < num; i++) { 125 if (alarm_fired) 126 break; 127 getppid(); 128 } 129 benchmark_stop(); 130 return (i); 131} 132 133uintmax_t 134test_getresuid(uintmax_t num, uintmax_t int_arg, const char *path) 135{ 136 uid_t ruid, euid, suid; 137 uintmax_t i; 138 139 benchmark_start(); 140 for (i = 0; i < num; i++) { 141 if (alarm_fired) 142 break; 143 (void)getresuid(&ruid, &euid, &suid); 144 } 145 benchmark_stop(); 146 return (i); 147} 148 149uintmax_t 150test_clock_gettime(uintmax_t num, uintmax_t int_arg, const char *path) 151{ 152 struct timespec ts; 153 uintmax_t i; 154 155 benchmark_start(); 156 for (i = 0; i < num; i++) { 157 if (alarm_fired) 158 break; 159 (void)clock_gettime(CLOCK_REALTIME, &ts); 160 } 161 benchmark_stop(); 162 return (i); 163} 164 165uintmax_t 166test_gettimeofday(uintmax_t num, uintmax_t int_arg, const char *path) 167{ 168 struct timeval tv; 169 uintmax_t i; 170 171 benchmark_start(); 172 for (i = 0; i < num; i++) { 173 if (alarm_fired) 174 break; 175 (void)gettimeofday(&tv, NULL); 176 } 177 benchmark_stop(); 178 return (i); 179} 180 181uintmax_t 182test_getpriority(uintmax_t num, uintmax_t int_arg, const char *path) 183{ 184 uintmax_t i; 185 186 benchmark_start(); 187 for (i = 0; i < num; i++) { 188 if (alarm_fired) 189 break; 190 (void)getpriority(PRIO_PROCESS, 0); 191 } 192 benchmark_stop(); 193 return (i); 194} 195 196uintmax_t 197test_pipe(uintmax_t num, uintmax_t int_arg, const char *path) 198{ 199 int fd[2], i; 200 201 /* 202 * pipe creation is expensive, as it will allocate a new file 203 * descriptor, allocate a new pipe, hook it all up, and return. 204 * Destroying is also expensive, as we now have to free up 205 * the file descriptors and return the pipe. 206 */ 207 if (pipe(fd) < 0) 208 err(-1, "test_pipe: pipe"); 209 close(fd[0]); 210 close(fd[1]); 211 benchmark_start(); 212 for (i = 0; i < num; i++) { 213 if (alarm_fired) 214 break; 215 if (pipe(fd) == -1) 216 err(-1, "test_pipe: pipe"); 217 close(fd[0]); 218 close(fd[1]); 219 } 220 benchmark_stop(); 221 return (i); 222} 223 224uintmax_t 225test_select(uintmax_t num, uintmax_t int_arg, const char *path) 226{ 227 fd_set readfds, writefds, exceptfds; 228 struct timeval tv; 229 uintmax_t i; 230 int error; 231 232 FD_ZERO(&readfds); 233 FD_ZERO(&writefds); 234 FD_ZERO(&exceptfds); 235 236 tv.tv_sec = 0; 237 tv.tv_usec = 0; 238 239 benchmark_start(); 240 for (i = 0; i < num; i++) { 241 if (alarm_fired) 242 break; 243 (void)select(0, &readfds, &writefds, &exceptfds, &tv); 244 } 245 benchmark_stop(); 246 return (i); 247} 248 249uintmax_t 250test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path) 251{ 252 uintmax_t i; 253 int so; 254 255 so = socket(int_arg, SOCK_STREAM, 0); 256 if (so < 0) 257 err(-1, "test_socket_stream: socket"); 258 close(so); 259 benchmark_start(); 260 for (i = 0; i < num; i++) { 261 if (alarm_fired) 262 break; 263 so = socket(int_arg, SOCK_STREAM, 0); 264 if (so == -1) 265 err(-1, "test_socket_stream: socket"); 266 close(so); 267 } 268 benchmark_stop(); 269 return (i); 270} 271 272uintmax_t 273test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path) 274{ 275 uintmax_t i; 276 int so; 277 278 so = socket(int_arg, SOCK_DGRAM, 0); 279 if (so < 0) 280 err(-1, "test_socket_dgram: socket"); 281 close(so); 282 benchmark_start(); 283 for (i = 0; i < num; i++) { 284 if (alarm_fired) 285 break; 286 so = socket(int_arg, SOCK_DGRAM, 0); 287 if (so == -1) 288 err(-1, "test_socket_dgram: socket"); 289 close(so); 290 } 291 benchmark_stop(); 292 return (i); 293} 294 295uintmax_t 296test_socketpair_stream(uintmax_t num, uintmax_t int_arg, const char *path) 297{ 298 uintmax_t i; 299 int so[2]; 300 301 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1) 302 err(-1, "test_socketpair_stream: socketpair"); 303 close(so[0]); 304 close(so[1]); 305 benchmark_start(); 306 for (i = 0; i < num; i++) { 307 if (alarm_fired) 308 break; 309 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1) 310 err(-1, "test_socketpair_stream: socketpair"); 311 close(so[0]); 312 close(so[1]); 313 } 314 benchmark_stop(); 315 return (i); 316} 317 318uintmax_t 319test_socketpair_dgram(uintmax_t num, uintmax_t int_arg, const char *path) 320{ 321 uintmax_t i; 322 int so[2]; 323 324 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1) 325 err(-1, "test_socketpair_dgram: socketpair"); 326 close(so[0]); 327 close(so[1]); 328 benchmark_start(); 329 for (i = 0; i < num; i++) { 330 if (alarm_fired) 331 break; 332 if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1) 333 err(-1, "test_socketpair_dgram: socketpair"); 334 close(so[0]); 335 close(so[1]); 336 } 337 benchmark_stop(); 338 return (i); 339} 340 341uintmax_t 342test_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path) 343{ 344 uintmax_t i; 345 int fd; 346 347 (void)unlink(path); 348 fd = open(path, O_RDWR | O_CREAT, 0600); 349 if (fd < 0) 350 err(-1, "test_create_unlink: create: %s", path); 351 close(fd); 352 if (unlink(path) < 0) 353 err(-1, "test_create_unlink: unlink: %s", path); 354 benchmark_start(); 355 for (i = 0; i < num; i++) { 356 if (alarm_fired) 357 break; 358 fd = open(path, O_RDWR | O_CREAT, 0600); 359 if (fd < 0) 360 err(-1, "test_create_unlink: create: %s", path); 361 close(fd); 362 if (unlink(path) < 0) 363 err(-1, "test_create_unlink: unlink: %s", path); 364 } 365 benchmark_stop(); 366 return (i); 367} 368 369uintmax_t 370test_open_close(uintmax_t num, uintmax_t int_arg, const char *path) 371{ 372 uintmax_t i; 373 int fd; 374 375 fd = open(path, O_RDONLY); 376 if (fd < 0) 377 err(-1, "test_open_close: %s", path); 378 close(fd); 379 380 benchmark_start(); 381 for (i = 0; i < num; i++) { 382 if (alarm_fired) 383 break; 384 fd = open(path, O_RDONLY); 385 if (fd < 0) 386 err(-1, "test_open_close: %s", path); 387 close(fd); 388 } 389 benchmark_stop(); 390 return (i); 391} 392 393uintmax_t 394test_bad_open(uintmax_t num, uintmax_t int_arg, const char *path) 395{ 396 uintmax_t i; 397 398 benchmark_start(); 399 for (i = 0; i < num; i++) { 400 if (alarm_fired) 401 break; 402 open("", O_RDONLY); 403 } 404 benchmark_stop(); 405 return (i); 406} 407 408uintmax_t 409test_read(uintmax_t num, uintmax_t int_arg, const char *path) 410{ 411 char buf[int_arg]; 412 uintmax_t i; 413 int fd; 414 415 fd = open(path, O_RDONLY); 416 if (fd < 0) 417 err(-1, "test_open_read: %s", path); 418 (void)pread(fd, buf, int_arg, 0); 419 420 benchmark_start(); 421 for (i = 0; i < num; i++) { 422 if (alarm_fired) 423 break; 424 (void)pread(fd, buf, int_arg, 0); 425 } 426 benchmark_stop(); 427 close(fd); 428 return (i); 429} 430 431uintmax_t 432test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path) 433{ 434 char buf[int_arg]; 435 uintmax_t i; 436 int fd; 437 438 fd = open(path, O_RDONLY); 439 if (fd < 0) 440 err(-1, "test_open_read_close: %s", path); 441 (void)read(fd, buf, int_arg); 442 close(fd); 443 444 benchmark_start(); 445 for (i = 0; i < num; i++) { 446 if (alarm_fired) 447 break; 448 fd = open(path, O_RDONLY); 449 if (fd < 0) 450 err(-1, "test_open_read_close: %s", path); 451 (void)read(fd, buf, int_arg); 452 close(fd); 453 } 454 benchmark_stop(); 455 return (i); 456} 457 458uintmax_t 459test_dup(uintmax_t num, uintmax_t int_arg, const char *path) 460{ 461 int fd, i, shmfd; 462 463 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 464 if (shmfd < 0) 465 err(-1, "test_dup: shm_open"); 466 fd = dup(shmfd); 467 if (fd >= 0) 468 close(fd); 469 benchmark_start(); 470 for (i = 0; i < num; i++) { 471 if (alarm_fired) 472 break; 473 fd = dup(shmfd); 474 if (fd >= 0) 475 close(fd); 476 } 477 benchmark_stop(); 478 close(shmfd); 479 return (i); 480} 481 482uintmax_t 483test_shmfd(uintmax_t num, uintmax_t int_arg, const char *path) 484{ 485 uintmax_t i; 486 int shmfd; 487 488 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 489 if (shmfd < 0) 490 err(-1, "test_shmfd: shm_open"); 491 close(shmfd); 492 benchmark_start(); 493 for (i = 0; i < num; i++) { 494 if (alarm_fired) 495 break; 496 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 497 if (shmfd < 0) 498 err(-1, "test_shmfd: shm_open"); 499 close(shmfd); 500 } 501 benchmark_stop(); 502 return (i); 503} 504 505uintmax_t 506test_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path) 507{ 508 struct stat sb; 509 uintmax_t i; 510 int shmfd; 511 512 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 513 if (shmfd < 0) 514 err(-1, "test_fstat_shmfd: shm_open"); 515 if (fstat(shmfd, &sb) < 0) 516 err(-1, "test_fstat_shmfd: fstat"); 517 benchmark_start(); 518 for (i = 0; i < num; i++) { 519 if (alarm_fired) 520 break; 521 (void)fstat(shmfd, &sb); 522 } 523 benchmark_stop(); 524 close(shmfd); 525 return (i); 526} 527 528uintmax_t 529test_fork(uintmax_t num, uintmax_t int_arg, const char *path) 530{ 531 pid_t pid; 532 uintmax_t i; 533 534 pid = fork(); 535 if (pid < 0) 536 err(-1, "test_fork: fork"); 537 if (pid == 0) 538 _exit(0); 539 if (waitpid(pid, NULL, 0) < 0) 540 err(-1, "test_fork: waitpid"); 541 benchmark_start(); 542 for (i = 0; i < num; i++) { 543 if (alarm_fired) 544 break; 545 pid = fork(); 546 if (pid < 0) 547 err(-1, "test_fork: fork"); 548 if (pid == 0) 549 _exit(0); 550 if (waitpid(pid, NULL, 0) < 0) 551 err(-1, "test_fork: waitpid"); 552 } 553 benchmark_stop(); 554 return (i); 555} 556 557uintmax_t 558test_vfork(uintmax_t num, uintmax_t int_arg, const char *path) 559{ 560 pid_t pid; 561 uintmax_t i; 562 563 pid = vfork(); 564 if (pid < 0) 565 err(-1, "test_vfork: vfork"); 566 if (pid == 0) 567 _exit(0); 568 if (waitpid(pid, NULL, 0) < 0) 569 err(-1, "test_vfork: waitpid"); 570 benchmark_start(); 571 for (i = 0; i < num; i++) { 572 if (alarm_fired) 573 break; 574 pid = vfork(); 575 if (pid < 0) 576 err(-1, "test_vfork: vfork"); 577 if (pid == 0) 578 _exit(0); 579 if (waitpid(pid, NULL, 0) < 0) 580 err(-1, "test_vfork: waitpid"); 581 } 582 benchmark_stop(); 583 return (i); 584} 585 586#define USR_BIN_TRUE "/usr/bin/true" 587static char *execve_args[] = { USR_BIN_TRUE, NULL}; 588extern char **environ; 589 590uintmax_t 591test_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path) 592{ 593 pid_t pid; 594 uintmax_t i; 595 596 pid = fork(); 597 if (pid < 0) 598 err(-1, "test_fork_exec: fork"); 599 if (pid == 0) { 600 (void)execve(USR_BIN_TRUE, execve_args, environ); 601 err(-1, "execve"); 602 } 603 if (waitpid(pid, NULL, 0) < 0) 604 err(-1, "test_fork: waitpid"); 605 benchmark_start(); 606 for (i = 0; i < num; i++) { 607 if (alarm_fired) 608 break; 609 pid = fork(); 610 if (pid < 0) 611 err(-1, "test_fork_exec: fork"); 612 if (pid == 0) { 613 (void)execve(USR_BIN_TRUE, execve_args, environ); 614 err(-1, "test_fork_exec: execve"); 615 } 616 if (waitpid(pid, NULL, 0) < 0) 617 err(-1, "test_fork_exec: waitpid"); 618 } 619 benchmark_stop(); 620 return (i); 621} 622 623uintmax_t 624test_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path) 625{ 626 pid_t pid; 627 uintmax_t i; 628 629 pid = vfork(); 630 if (pid < 0) 631 err(-1, "test_vfork_exec: vfork"); 632 if (pid == 0) { 633 (void)execve(USR_BIN_TRUE, execve_args, environ); 634 err(-1, "test_vfork_exec: execve"); 635 } 636 if (waitpid(pid, NULL, 0) < 0) 637 err(-1, "test_vfork_exec: waitpid"); 638 benchmark_start(); 639 for (i = 0; i < num; i++) { 640 if (alarm_fired) 641 break; 642 pid = vfork(); 643 if (pid < 0) 644 err(-1, "test_vfork_exec: vfork"); 645 if (pid == 0) { 646 (void)execve(USR_BIN_TRUE, execve_args, environ); 647 err(-1, "execve"); 648 } 649 if (waitpid(pid, NULL, 0) < 0) 650 err(-1, "test_vfork_exec: waitpid"); 651 } 652 benchmark_stop(); 653 return (i); 654} 655 656uintmax_t 657test_chroot(uintmax_t num, uintmax_t int_arg, const char *path) 658{ 659 uintmax_t i; 660 661 if (chroot("/") < 0) 662 err(-1, "test_chroot: chroot"); 663 benchmark_start(); 664 for (i = 0; i < num; i++) { 665 if (alarm_fired) 666 break; 667 if (chroot("/") < 0) 668 err(-1, "test_chroot: chroot"); 669 } 670 benchmark_stop(); 671 return (i); 672} 673 674uintmax_t 675test_setuid(uintmax_t num, uintmax_t int_arg, const char *path) 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 for (i = 0; i < num; i++) { 685 if (alarm_fired) 686 break; 687 if (setuid(uid) < 0) 688 err(-1, "test_setuid: setuid"); 689 } 690 benchmark_stop(); 691 return (i); 692} 693 694struct test { 695 const char *t_name; 696 uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *); 697 int t_flags; 698 uintmax_t t_int; 699}; 700 701#define FLAG_PATH 0x00000001 702 703static const struct test tests[] = { 704 { "getuid", test_getuid }, 705 { "getppid", test_getppid }, 706 { "getresuid", test_getresuid }, 707 { "clock_gettime", test_clock_gettime }, 708 { "gettimeofday", test_gettimeofday }, 709 { "getpriority", test_getpriority }, 710 { "pipe", test_pipe }, 711 { "select", test_select }, 712 { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL }, 713 { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL }, 714 { "socketpair_stream", test_socketpair_stream }, 715 { "socketpair_dgram", test_socketpair_dgram }, 716 { "socket_tcp", test_socket_stream, .t_int = PF_INET }, 717 { "socket_udp", test_socket_dgram, .t_int = PF_INET }, 718 { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH }, 719 { "bad_open", test_bad_open }, 720 { "open_close", test_open_close, .t_flags = FLAG_PATH }, 721 { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH, 722 .t_int = 1 }, 723 { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH, 724 .t_int = 10 }, 725 { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH, 726 .t_int = 100 }, 727 { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH, 728 .t_int = 1000 }, 729 { "open_read_close_10000", test_open_read_close, 730 .t_flags = FLAG_PATH, .t_int = 10000 }, 731 { "open_read_close_100000", test_open_read_close, 732 .t_flags = FLAG_PATH, .t_int = 100000 }, 733 { "open_read_close_1000000", test_open_read_close, 734 .t_flags = FLAG_PATH, .t_int = 1000000 }, 735 { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 }, 736 { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 }, 737 { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 }, 738 { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 }, 739 { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 }, 740 { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 }, 741 { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 }, 742 { "dup", test_dup }, 743 { "shmfd", test_shmfd }, 744 { "fstat_shmfd", test_fstat_shmfd }, 745 { "fork", test_fork }, 746 { "vfork", test_vfork }, 747 { "fork_exec", test_fork_exec }, 748 { "vfork_exec", test_vfork_exec }, 749 { "chroot", test_chroot }, 750 { "setuid", test_setuid }, 751}; 752static const int tests_count = sizeof(tests) / sizeof(tests[0]); 753 754static void 755usage(void) 756{ 757 int i; 758 759 fprintf(stderr, "syscall_timing [-i iterations] [-l loops] " 760 "[-p path] [-s seconds] test\n"); 761 for (i = 0; i < tests_count; i++) 762 fprintf(stderr, " %s\n", tests[i].t_name); 763 exit(-1); 764} 765 766int 767main(int argc, char *argv[]) 768{ 769 struct timespec ts_res; 770 const struct test *the_test; 771 const char *path; 772 long long ll; 773 char *endp; 774 int ch, error, i, j, k; 775 uintmax_t iterations, loops; 776 777 alarm_timeout = 1; 778 iterations = 0; 779 loops = 10; 780 path = NULL; 781 while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) { 782 switch (ch) { 783 case 'i': 784 ll = strtol(optarg, &endp, 10); 785 if (*endp != 0 || ll < 1) 786 usage(); 787 iterations = ll; 788 break; 789 790 case 'l': 791 ll = strtol(optarg, &endp, 10); 792 if (*endp != 0 || ll < 1 || ll > 100000) 793 usage(); 794 loops = ll; 795 break; 796 797 case 'p': 798 path = optarg; 799 break; 800 801 case 's': 802 ll = strtol(optarg, &endp, 10); 803 if (*endp != 0 || ll < 1 || ll > 60*60) 804 usage(); 805 alarm_timeout = ll; 806 break; 807 808 case '?': 809 default: 810 usage(); 811 } 812 } 813 argc -= optind; 814 argv += optind; 815 816 if (iterations < 1 && alarm_timeout < 1) 817 usage(); 818 if (iterations < 1) 819 iterations = UINT64_MAX; 820 if (loops < 1) 821 loops = 1; 822 823 if (argc < 1) 824 usage(); 825 826 /* 827 * Validate test list and that, if a path is required, it is 828 * defined. 829 */ 830 for (j = 0; j < argc; j++) { 831 the_test = NULL; 832 for (i = 0; i < tests_count; i++) { 833 if (strcmp(argv[j], tests[i].t_name) == 0) 834 the_test = &tests[i]; 835 } 836 if (the_test == NULL) 837 usage(); 838 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) { 839 errx(-1, "%s requires -p", the_test->t_name); 840 } 841 } 842 843 error = clock_getres(CLOCK_REALTIME, &ts_res); 844 assert(error == 0); 845 printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec, 846 (uintmax_t)ts_res.tv_nsec); 847 printf("test\tloop\ttime\titerations\tperiteration\n"); 848 849 for (j = 0; j < argc; j++) { 850 uintmax_t calls, nsecsperit; 851 852 the_test = NULL; 853 for (i = 0; i < tests_count; i++) { 854 if (strcmp(argv[j], tests[i].t_name) == 0) 855 the_test = &tests[i]; 856 } 857 858 /* 859 * Run one warmup, then do the real thing (loops) times. 860 */ 861 the_test->t_func(iterations, the_test->t_int, path); 862 calls = 0; 863 for (k = 0; k < loops; k++) { 864 calls = the_test->t_func(iterations, the_test->t_int, 865 path); 866 timespecsub(&ts_end, &ts_start); 867 printf("%s\t%d\t", the_test->t_name, k); 868 printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec, 869 (uintmax_t)ts_end.tv_nsec, calls); 870 871 /* 872 * Note. This assumes that each iteration takes less than 873 * a second, and that our total nanoseconds doesn't exceed 874 * the room in our arithmetic unit. Fine for system calls, 875 * but not for long things. 876 */ 877 nsecsperit = ts_end.tv_sec * 1000000000; 878 nsecsperit += ts_end.tv_nsec; 879 nsecsperit /= calls; 880 printf("0.%09ju\n", (uintmax_t)nsecsperit); 881 } 882 } 883 return (0); 884} 885