syscall_timing.c revision 332627
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 332627 2018-04-16 17:29:50Z 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_access(uintmax_t num, uintmax_t int_arg, const char *path) 343{ 344 uintmax_t i; 345 int fd; 346 347 fd = access(path, O_RDONLY); 348 if (fd < 0) 349 err(-1, "test_access: %s", path); 350 close(fd); 351 352 benchmark_start(); 353 for (i = 0; i < num; i++) { 354 if (alarm_fired) 355 break; 356 access(path, O_RDONLY); 357 close(fd); 358 } 359 benchmark_stop(); 360 return (i); 361} 362 363uintmax_t 364test_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path) 365{ 366 uintmax_t i; 367 int fd; 368 369 (void)unlink(path); 370 fd = open(path, O_RDWR | O_CREAT, 0600); 371 if (fd < 0) 372 err(-1, "test_create_unlink: create: %s", path); 373 close(fd); 374 if (unlink(path) < 0) 375 err(-1, "test_create_unlink: unlink: %s", path); 376 benchmark_start(); 377 for (i = 0; i < num; i++) { 378 if (alarm_fired) 379 break; 380 fd = open(path, O_RDWR | O_CREAT, 0600); 381 if (fd < 0) 382 err(-1, "test_create_unlink: create: %s", path); 383 close(fd); 384 if (unlink(path) < 0) 385 err(-1, "test_create_unlink: unlink: %s", path); 386 } 387 benchmark_stop(); 388 return (i); 389} 390 391uintmax_t 392test_open_close(uintmax_t num, uintmax_t int_arg, const char *path) 393{ 394 uintmax_t i; 395 int fd; 396 397 fd = open(path, O_RDONLY); 398 if (fd < 0) 399 err(-1, "test_open_close: %s", path); 400 close(fd); 401 402 benchmark_start(); 403 for (i = 0; i < num; i++) { 404 if (alarm_fired) 405 break; 406 fd = open(path, O_RDONLY); 407 if (fd < 0) 408 err(-1, "test_open_close: %s", path); 409 close(fd); 410 } 411 benchmark_stop(); 412 return (i); 413} 414 415uintmax_t 416test_bad_open(uintmax_t num, uintmax_t int_arg, const char *path) 417{ 418 uintmax_t i; 419 420 benchmark_start(); 421 for (i = 0; i < num; i++) { 422 if (alarm_fired) 423 break; 424 open("", O_RDONLY); 425 } 426 benchmark_stop(); 427 return (i); 428} 429 430uintmax_t 431test_read(uintmax_t num, uintmax_t int_arg, const char *path) 432{ 433 char buf[int_arg]; 434 uintmax_t i; 435 int fd; 436 437 fd = open(path, O_RDONLY); 438 if (fd < 0) 439 err(-1, "test_open_read: %s", path); 440 (void)pread(fd, buf, int_arg, 0); 441 442 benchmark_start(); 443 for (i = 0; i < num; i++) { 444 if (alarm_fired) 445 break; 446 (void)pread(fd, buf, int_arg, 0); 447 } 448 benchmark_stop(); 449 close(fd); 450 return (i); 451} 452 453uintmax_t 454test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path) 455{ 456 char buf[int_arg]; 457 uintmax_t i; 458 int fd; 459 460 fd = open(path, O_RDONLY); 461 if (fd < 0) 462 err(-1, "test_open_read_close: %s", path); 463 (void)read(fd, buf, int_arg); 464 close(fd); 465 466 benchmark_start(); 467 for (i = 0; i < num; i++) { 468 if (alarm_fired) 469 break; 470 fd = open(path, O_RDONLY); 471 if (fd < 0) 472 err(-1, "test_open_read_close: %s", path); 473 (void)read(fd, buf, int_arg); 474 close(fd); 475 } 476 benchmark_stop(); 477 return (i); 478} 479 480uintmax_t 481test_dup(uintmax_t num, uintmax_t int_arg, const char *path) 482{ 483 int fd, i, shmfd; 484 485 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 486 if (shmfd < 0) 487 err(-1, "test_dup: shm_open"); 488 fd = dup(shmfd); 489 if (fd >= 0) 490 close(fd); 491 benchmark_start(); 492 for (i = 0; i < num; i++) { 493 if (alarm_fired) 494 break; 495 fd = dup(shmfd); 496 if (fd >= 0) 497 close(fd); 498 } 499 benchmark_stop(); 500 close(shmfd); 501 return (i); 502} 503 504uintmax_t 505test_shmfd(uintmax_t num, uintmax_t int_arg, const char *path) 506{ 507 uintmax_t i; 508 int shmfd; 509 510 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 511 if (shmfd < 0) 512 err(-1, "test_shmfd: shm_open"); 513 close(shmfd); 514 benchmark_start(); 515 for (i = 0; i < num; i++) { 516 if (alarm_fired) 517 break; 518 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 519 if (shmfd < 0) 520 err(-1, "test_shmfd: shm_open"); 521 close(shmfd); 522 } 523 benchmark_stop(); 524 return (i); 525} 526 527uintmax_t 528test_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path) 529{ 530 struct stat sb; 531 uintmax_t i; 532 int shmfd; 533 534 shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600); 535 if (shmfd < 0) 536 err(-1, "test_fstat_shmfd: shm_open"); 537 if (fstat(shmfd, &sb) < 0) 538 err(-1, "test_fstat_shmfd: fstat"); 539 benchmark_start(); 540 for (i = 0; i < num; i++) { 541 if (alarm_fired) 542 break; 543 (void)fstat(shmfd, &sb); 544 } 545 benchmark_stop(); 546 close(shmfd); 547 return (i); 548} 549 550uintmax_t 551test_fork(uintmax_t num, uintmax_t int_arg, const char *path) 552{ 553 pid_t pid; 554 uintmax_t i; 555 556 pid = fork(); 557 if (pid < 0) 558 err(-1, "test_fork: fork"); 559 if (pid == 0) 560 _exit(0); 561 if (waitpid(pid, NULL, 0) < 0) 562 err(-1, "test_fork: waitpid"); 563 benchmark_start(); 564 for (i = 0; i < num; i++) { 565 if (alarm_fired) 566 break; 567 pid = fork(); 568 if (pid < 0) 569 err(-1, "test_fork: fork"); 570 if (pid == 0) 571 _exit(0); 572 if (waitpid(pid, NULL, 0) < 0) 573 err(-1, "test_fork: waitpid"); 574 } 575 benchmark_stop(); 576 return (i); 577} 578 579uintmax_t 580test_vfork(uintmax_t num, uintmax_t int_arg, const char *path) 581{ 582 pid_t pid; 583 uintmax_t i; 584 585 pid = vfork(); 586 if (pid < 0) 587 err(-1, "test_vfork: vfork"); 588 if (pid == 0) 589 _exit(0); 590 if (waitpid(pid, NULL, 0) < 0) 591 err(-1, "test_vfork: waitpid"); 592 benchmark_start(); 593 for (i = 0; i < num; i++) { 594 if (alarm_fired) 595 break; 596 pid = vfork(); 597 if (pid < 0) 598 err(-1, "test_vfork: vfork"); 599 if (pid == 0) 600 _exit(0); 601 if (waitpid(pid, NULL, 0) < 0) 602 err(-1, "test_vfork: waitpid"); 603 } 604 benchmark_stop(); 605 return (i); 606} 607 608#define USR_BIN_TRUE "/usr/bin/true" 609static char *execve_args[] = { USR_BIN_TRUE, NULL}; 610extern char **environ; 611 612uintmax_t 613test_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path) 614{ 615 pid_t pid; 616 uintmax_t i; 617 618 pid = fork(); 619 if (pid < 0) 620 err(-1, "test_fork_exec: fork"); 621 if (pid == 0) { 622 (void)execve(USR_BIN_TRUE, execve_args, environ); 623 err(-1, "execve"); 624 } 625 if (waitpid(pid, NULL, 0) < 0) 626 err(-1, "test_fork: waitpid"); 627 benchmark_start(); 628 for (i = 0; i < num; i++) { 629 if (alarm_fired) 630 break; 631 pid = fork(); 632 if (pid < 0) 633 err(-1, "test_fork_exec: fork"); 634 if (pid == 0) { 635 (void)execve(USR_BIN_TRUE, execve_args, environ); 636 err(-1, "test_fork_exec: execve"); 637 } 638 if (waitpid(pid, NULL, 0) < 0) 639 err(-1, "test_fork_exec: waitpid"); 640 } 641 benchmark_stop(); 642 return (i); 643} 644 645uintmax_t 646test_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path) 647{ 648 pid_t pid; 649 uintmax_t i; 650 651 pid = vfork(); 652 if (pid < 0) 653 err(-1, "test_vfork_exec: vfork"); 654 if (pid == 0) { 655 (void)execve(USR_BIN_TRUE, execve_args, environ); 656 err(-1, "test_vfork_exec: execve"); 657 } 658 if (waitpid(pid, NULL, 0) < 0) 659 err(-1, "test_vfork_exec: waitpid"); 660 benchmark_start(); 661 for (i = 0; i < num; i++) { 662 if (alarm_fired) 663 break; 664 pid = vfork(); 665 if (pid < 0) 666 err(-1, "test_vfork_exec: vfork"); 667 if (pid == 0) { 668 (void)execve(USR_BIN_TRUE, execve_args, environ); 669 err(-1, "execve"); 670 } 671 if (waitpid(pid, NULL, 0) < 0) 672 err(-1, "test_vfork_exec: waitpid"); 673 } 674 benchmark_stop(); 675 return (i); 676} 677 678uintmax_t 679test_chroot(uintmax_t num, uintmax_t int_arg, const char *path) 680{ 681 uintmax_t i; 682 683 if (chroot("/") < 0) 684 err(-1, "test_chroot: chroot"); 685 benchmark_start(); 686 for (i = 0; i < num; i++) { 687 if (alarm_fired) 688 break; 689 if (chroot("/") < 0) 690 err(-1, "test_chroot: chroot"); 691 } 692 benchmark_stop(); 693 return (i); 694} 695 696uintmax_t 697test_setuid(uintmax_t num, uintmax_t int_arg, const char *path) 698{ 699 uid_t uid; 700 uintmax_t i; 701 702 uid = getuid(); 703 if (setuid(uid) < 0) 704 err(-1, "test_setuid: setuid"); 705 benchmark_start(); 706 for (i = 0; i < num; i++) { 707 if (alarm_fired) 708 break; 709 if (setuid(uid) < 0) 710 err(-1, "test_setuid: setuid"); 711 } 712 benchmark_stop(); 713 return (i); 714} 715 716struct test { 717 const char *t_name; 718 uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *); 719 int t_flags; 720 uintmax_t t_int; 721}; 722 723#define FLAG_PATH 0x00000001 724 725static const struct test tests[] = { 726 { "getuid", test_getuid }, 727 { "getppid", test_getppid }, 728 { "getresuid", test_getresuid }, 729 { "clock_gettime", test_clock_gettime }, 730 { "gettimeofday", test_gettimeofday }, 731 { "getpriority", test_getpriority }, 732 { "pipe", test_pipe }, 733 { "select", test_select }, 734 { "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL }, 735 { "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL }, 736 { "socketpair_stream", test_socketpair_stream }, 737 { "socketpair_dgram", test_socketpair_dgram }, 738 { "socket_tcp", test_socket_stream, .t_int = PF_INET }, 739 { "socket_udp", test_socket_dgram, .t_int = PF_INET }, 740 { "access", test_access, .t_flags = FLAG_PATH }, 741 { "create_unlink", test_create_unlink, .t_flags = FLAG_PATH }, 742 { "bad_open", test_bad_open }, 743 { "open_close", test_open_close, .t_flags = FLAG_PATH }, 744 { "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH, 745 .t_int = 1 }, 746 { "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH, 747 .t_int = 10 }, 748 { "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH, 749 .t_int = 100 }, 750 { "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH, 751 .t_int = 1000 }, 752 { "open_read_close_10000", test_open_read_close, 753 .t_flags = FLAG_PATH, .t_int = 10000 }, 754 { "open_read_close_100000", test_open_read_close, 755 .t_flags = FLAG_PATH, .t_int = 100000 }, 756 { "open_read_close_1000000", test_open_read_close, 757 .t_flags = FLAG_PATH, .t_int = 1000000 }, 758 { "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 }, 759 { "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 }, 760 { "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 }, 761 { "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 }, 762 { "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 }, 763 { "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 }, 764 { "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 }, 765 { "dup", test_dup }, 766 { "shmfd", test_shmfd }, 767 { "fstat_shmfd", test_fstat_shmfd }, 768 { "fork", test_fork }, 769 { "vfork", test_vfork }, 770 { "fork_exec", test_fork_exec }, 771 { "vfork_exec", test_vfork_exec }, 772 { "chroot", test_chroot }, 773 { "setuid", test_setuid }, 774}; 775static const int tests_count = sizeof(tests) / sizeof(tests[0]); 776 777static void 778usage(void) 779{ 780 int i; 781 782 fprintf(stderr, "syscall_timing [-i iterations] [-l loops] " 783 "[-p path] [-s seconds] test\n"); 784 for (i = 0; i < tests_count; i++) 785 fprintf(stderr, " %s\n", tests[i].t_name); 786 exit(-1); 787} 788 789int 790main(int argc, char *argv[]) 791{ 792 struct timespec ts_res; 793 const struct test *the_test; 794 const char *path; 795 long long ll; 796 char *endp; 797 int ch, error, i, j, k; 798 uintmax_t iterations, loops; 799 800 alarm_timeout = 1; 801 iterations = 0; 802 loops = 10; 803 path = NULL; 804 while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) { 805 switch (ch) { 806 case 'i': 807 ll = strtol(optarg, &endp, 10); 808 if (*endp != 0 || ll < 1) 809 usage(); 810 iterations = ll; 811 break; 812 813 case 'l': 814 ll = strtol(optarg, &endp, 10); 815 if (*endp != 0 || ll < 1 || ll > 100000) 816 usage(); 817 loops = ll; 818 break; 819 820 case 'p': 821 path = optarg; 822 break; 823 824 case 's': 825 ll = strtol(optarg, &endp, 10); 826 if (*endp != 0 || ll < 1 || ll > 60*60) 827 usage(); 828 alarm_timeout = ll; 829 break; 830 831 case '?': 832 default: 833 usage(); 834 } 835 } 836 argc -= optind; 837 argv += optind; 838 839 if (iterations < 1 && alarm_timeout < 1) 840 usage(); 841 if (iterations < 1) 842 iterations = UINT64_MAX; 843 if (loops < 1) 844 loops = 1; 845 846 if (argc < 1) 847 usage(); 848 849 /* 850 * Validate test list and that, if a path is required, it is 851 * defined. 852 */ 853 for (j = 0; j < argc; j++) { 854 the_test = NULL; 855 for (i = 0; i < tests_count; i++) { 856 if (strcmp(argv[j], tests[i].t_name) == 0) 857 the_test = &tests[i]; 858 } 859 if (the_test == NULL) 860 usage(); 861 if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) { 862 errx(-1, "%s requires -p", the_test->t_name); 863 } 864 } 865 866 error = clock_getres(CLOCK_REALTIME, &ts_res); 867 assert(error == 0); 868 printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec, 869 (uintmax_t)ts_res.tv_nsec); 870 printf("test\tloop\ttime\titerations\tperiteration\n"); 871 872 for (j = 0; j < argc; j++) { 873 uintmax_t calls, nsecsperit; 874 875 the_test = NULL; 876 for (i = 0; i < tests_count; i++) { 877 if (strcmp(argv[j], tests[i].t_name) == 0) 878 the_test = &tests[i]; 879 } 880 881 /* 882 * Run one warmup, then do the real thing (loops) times. 883 */ 884 the_test->t_func(iterations, the_test->t_int, path); 885 calls = 0; 886 for (k = 0; k < loops; k++) { 887 calls = the_test->t_func(iterations, the_test->t_int, 888 path); 889 timespecsub(&ts_end, &ts_start); 890 printf("%s\t%d\t", the_test->t_name, k); 891 printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec, 892 (uintmax_t)ts_end.tv_nsec, calls); 893 894 /* 895 * Note. This assumes that each iteration takes less than 896 * a second, and that our total nanoseconds doesn't exceed 897 * the room in our arithmetic unit. Fine for system calls, 898 * but not for long things. 899 */ 900 nsecsperit = ts_end.tv_sec * 1000000000; 901 nsecsperit += ts_end.tv_nsec; 902 nsecsperit /= calls; 903 printf("0.%09ju\n", (uintmax_t)nsecsperit); 904 } 905 } 906 return (0); 907} 908