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