X-Git-Url: https://git.saurik.com/apple/shell_cmds.git/blobdiff_plain/26812dd2205b1e490a62d45c74d0b14e5eb24ad7..47d9aef863d2b1c3e210a0137c855acd0210a43f:/time/time.c?ds=sidebyside diff --git a/time/time.c b/time/time.c index 0a3cb47..afaaed0 100644 --- a/time/time.c +++ b/time/time.c @@ -33,52 +33,48 @@ * SUCH DAMAGE. */ +#include +#include +#include +#include +#include +#include #include -#ifndef lint -__COPYRIGHT("@(#) Copyright (c) 1987, 1988, 1993\n\ - The Regents of the University of California. All rights reserved.\n"); -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)time.c 8.1 (Berkeley) 6/6/93"; -#endif -__RCSID("$NetBSD: time.c,v 1.9 1997/10/20 03:28:21 lukem Exp $"); -#endif /* not lint */ - +#include #include #include #include #include #include +#include #include +#include #include +#include #include -#include -#include -#include int lflag; int portableflag; +bool child_running = true; -int main __P((int, char **)); +void +child_handler(int sig) +{ + child_running = false; +} int -main(argc, argv) - int argc; - char **argv; +main(int argc, char **argv) { int pid; - int ch, status; - struct timeval before, after; + int ch, status, rusage_ret = -1; + uint64_t before_ns, after_ns, duration_ns, duration_secs, duration_frac_ns; struct rusage ru; - char * radix = NULL; + struct rusage_info_v4 ruinfo; + sigset_t sigmask, suspmask, origmask; -#ifdef __GNUC__ /* XXX: borken gcc */ - (void)&argv; -#endif lflag = 0; - while ((ch = getopt(argc, argv, "lp")) != -1) + while ((ch = getopt(argc, argv, "lp")) != -1) { switch((char)ch) { case 'p': portableflag = 1; @@ -88,37 +84,88 @@ main(argc, argv) break; case '?': default: - fprintf(stderr, "usage: time [-lp] command.\n"); + fprintf(stderr, "usage: time [-lp] \n"); exit(1); } + } - if (!(argc -= optind)) + if (!(argc -= optind)) { exit(0); + } argv += optind; - gettimeofday(&before, (struct timezone *)NULL); - switch(pid = vfork()) { - case -1: /* error */ - perror("time"); - exit(1); - /* NOTREACHED */ - case 0: /* child */ + sigemptyset(&sigmask); + /* + * Block SIGCHLD so that the check for `child_running` doesn't miss the + * handler before calling `sigsuspend` and blocking forever. + */ + sigaddset(&sigmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigmask, &origmask); + + /* + * Ensure child signals are handled by the parent prior to fork; otherwise, + * they could be missed between the child forking and calling `sigsuspend`. + */ + (void)signal(SIGCHLD, child_handler); + + sigemptyset(&suspmask); + + before_ns = clock_gettime_nsec_np(CLOCK_UPTIME_RAW); + /* + * NB: Don't add anything between these two lines -- measurement is + * happening now. + */ + switch (pid = vfork()) { + case -1: /* error */ + err(EX_OSERR, "time"); + __builtin_unreachable(); + case 0: /* child */ + /* + * Allow the child to respond to signals by resetting to the original + * signal handling behavior. + */ + (void)sigprocmask(SIG_SETMASK, &origmask, NULL); execvp(*argv, argv); perror(*argv); _exit((errno == ENOENT) ? 127 : 126); - /* NOTREACHED */ + __builtin_unreachable(); + default: /* parent */ + break; } - /* parent */ + /* + * Let the child handle signals that normally exit. + */ (void)signal(SIGINT, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); - while (wait3(&status, 0, &ru) != pid); - gettimeofday(&after, (struct timezone *)NULL); - if (!WIFEXITED(status)) + + while (child_running) { + /* + * This would be racy, but SIGCHLD is blocked above (as part of + * `sigmask`. + */ + sigsuspend(&suspmask); + } + /* + * NB: Minimize what's added between these statements to preserve the + * accuracy of the time measurement. + */ + after_ns = clock_gettime_nsec_np(CLOCK_UPTIME_RAW); + if (lflag) { + rusage_ret = proc_pid_rusage(pid, RUSAGE_INFO_V4, (void **)&ruinfo); + } + while (wait3(&status, 0, &ru) != pid) { + } + if (!WIFEXITED(status)) { fprintf(stderr, "Command terminated abnormally.\n"); - timersub(&after, &before, &after); + } + duration_ns = after_ns - before_ns; + duration_secs = duration_ns / (1000 * 1000 * 1000); + duration_frac_ns = duration_ns - (duration_secs * 1000 * 1000 * 1000); if (portableflag) { + char *radix = NULL; + setlocale(LC_ALL, ""); radix = nl_langinfo(RADIXCHAR); @@ -126,15 +173,15 @@ main(argc, argv) radix = "."; } - fprintf (stderr, "real %9ld%s%02ld\n", - (long)after.tv_sec, radix, (long)after.tv_usec/10000); - fprintf (stderr, "user %9ld%s%02ld\n", + fprintf(stderr, "real %9" PRIu64 "%s%02" PRIu64 "\n", + duration_secs, radix, duration_frac_ns / (10 * 1000 * 1000)); + fprintf(stderr, "user %9ld%s%02ld\n", (long)ru.ru_utime.tv_sec, radix, (long)ru.ru_utime.tv_usec/10000); - fprintf (stderr, "sys %9ld%s%02ld\n", + fprintf(stderr, "sys %9ld%s%02ld\n", (long)ru.ru_stime.tv_sec, radix, (long)ru.ru_stime.tv_usec/10000); } else { - fprintf(stderr, "%9ld.%02ld real ", - (long)after.tv_sec, (long)after.tv_usec/10000); + fprintf(stderr, "%9" PRIu64 ".%02" PRIu64 " real ", + duration_secs, duration_frac_ns / (10 * 1000 * 1000)); fprintf(stderr, "%9ld.%02ld user ", (long)ru.ru_utime.tv_sec, (long)ru.ru_utime.tv_usec/10000); fprintf(stderr, "%9ld.%02ld sys\n", @@ -142,41 +189,57 @@ main(argc, argv) } if (lflag) { - int hz = 100; /* XXX */ + int hz = 100; /* XXX */ long ticks; ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) + hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000; - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_maxrss, "maximum resident set size"); - fprintf(stderr, "%10ld %s\n", ticks ? ru.ru_ixrss / ticks : 0, + fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_ixrss / ticks : 0, "average shared memory size"); - fprintf(stderr, "%10ld %s\n", ticks ? ru.ru_idrss / ticks : 0, + fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_idrss / ticks : 0, "average unshared data size"); - fprintf(stderr, "%10ld %s\n", ticks ? ru.ru_isrss / ticks : 0, + fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_isrss / ticks : 0, "average unshared stack size"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_minflt, "page reclaims"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_majflt, "page faults"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_nswap, "swaps"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_inblock, "block input operations"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_oublock, "block output operations"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_msgsnd, "messages sent"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_msgrcv, "messages received"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_nsignals, "signals received"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_nvcsw, "voluntary context switches"); - fprintf(stderr, "%10ld %s\n", + fprintf(stderr, "%20ld %s\n", ru.ru_nivcsw, "involuntary context switches"); + + if (rusage_ret >= 0) { + if (ruinfo.ri_instructions > 0) { + fprintf(stderr, "%20" PRIu64 " %s\n", ruinfo.ri_instructions, + "instructions retired"); + } + if (ruinfo.ri_cycles > 0) { + fprintf(stderr, "%20" PRIu64 " %s\n", ruinfo.ri_cycles, + "cycles elapsed"); + } + if (ruinfo.ri_lifetime_max_phys_footprint > 0) { + fprintf(stderr, "%20" PRIu64 " %s\n", + ruinfo.ri_lifetime_max_phys_footprint, + "peak memory footprint"); + } + } } - exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); + exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); }