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