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