/* $NetBSD: time.c,v 1.23 2017/07/15 14:34:08 christos Exp $ */ /* * Copyright (c) 1987, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "strpct.h" static void usage(void); static void prl(long, const char *); static void prts(const char *, const char *, const struct timespec *, const char *); static void prtv(const char *, const char *, const struct timeval *, const char *); void prusage1(FILE *, const char *fmt, struct rusage *, struct rusage *, struct timespec *, struct timespec *); static void pdeltat(FILE *fp, struct timeval *t1, struct timeval *t0); static void pcsecs(FILE *fp, long l); int main(int argc, char ** volatile argv) { int pid; int ch, status; int volatile portableflag; int volatile lflag; const char *decpt; const char *fmt; const struct lconv *lconv; struct timespec before, after; struct rusage ru; (void)setlocale(LC_ALL, ""); lflag = portableflag = 0; fmt = NULL; while ((ch = getopt(argc, argv, "cf:lp")) != -1) { switch (ch) { case 'f': fmt = optarg; portableflag = 0; lflag = 0; break; case 'c': fmt = "%Uu %Ss %E %P %X+%Dk %I+%Oio %Fpf+%Ww"; portableflag = 0; lflag = 0; break; case 'p': portableflag = 1; fmt = NULL; lflag = 0; break; case 'l': lflag = 1; portableflag = 0; fmt = NULL; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc < 1) usage(); (void)clock_gettime(CLOCK_MONOTONIC, &before); switch(pid = vfork()) { case -1: /* error */ err(EXIT_FAILURE, "Vfork failed"); /* NOTREACHED */ case 0: /* child */ /* LINTED will return only on failure */ execvp(*argv, argv); err((errno == ENOENT) ? 127 : 126, "Can't exec `%s'", *argv); /* NOTREACHED */ } /* parent */ (void)signal(SIGINT, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); if ((pid = wait4(pid, &status, 0, &ru)) == -1) err(EXIT_FAILURE, "wait4 %d failed", pid); (void)clock_gettime(CLOCK_MONOTONIC, &after); if (!WIFEXITED(status)) warnx("Command terminated abnormally."); timespecsub(&after, &before, &after); if ((lconv = localeconv()) == NULL || (decpt = lconv->decimal_point) == NULL) decpt = "."; if (fmt) { static struct rusage null_ru; before.tv_sec = 0; before.tv_nsec = 0; prusage1(stderr, fmt, &null_ru, &ru, &after, &before); } else if (portableflag) { prts("real ", decpt, &after, "\n"); prtv("user ", decpt, &ru.ru_utime, "\n"); prtv("sys ", decpt, &ru.ru_stime, "\n"); } else { prts("", decpt, &after, " real "); prtv("", decpt, &ru.ru_utime, " user "); prtv("", decpt, &ru.ru_stime, " sys\n"); } if (lflag) { int hz = (int)sysconf(_SC_CLK_TCK); unsigned long long ticks; #define SCALE(x) (long)(ticks ? x / ticks : 0) ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) + hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000; prl(ru.ru_maxrss, "maximum resident set size"); prl(SCALE(ru.ru_ixrss), "average shared memory size"); prl(SCALE(ru.ru_idrss), "average unshared data size"); prl(SCALE(ru.ru_isrss), "average unshared stack size"); prl(ru.ru_minflt, "page reclaims"); prl(ru.ru_majflt, "page faults"); prl(ru.ru_nswap, "swaps"); prl(ru.ru_inblock, "block input operations"); prl(ru.ru_oublock, "block output operations"); prl(ru.ru_msgsnd, "messages sent"); prl(ru.ru_msgrcv, "messages received"); prl(ru.ru_nsignals, "signals received"); prl(ru.ru_nvcsw, "voluntary context switches"); prl(ru.ru_nivcsw, "involuntary context switches"); } return (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); } static void usage(void) { (void)fprintf(stderr, "Usage: %s [-clp] [-f ] utility [argument ...]\n", getprogname()); exit(EXIT_FAILURE); } static void prl(long val, const char *expn) { (void)fprintf(stderr, "%10ld %s\n", val, expn); } static void prts(const char *pre, const char *decpt, const struct timespec *ts, const char *post) { (void)fprintf(stderr, "%s%9lld%s%02ld%s", pre, (long long)ts->tv_sec, decpt, (long)ts->tv_nsec / 10000000, post); } static void prtv(const char *pre, const char *decpt, const struct timeval *tv, const char *post) { (void)fprintf(stderr, "%s%9lld%s%02ld%s", pre, (long long)tv->tv_sec, decpt, (long)tv->tv_usec / 10000, post); } void prusage1(FILE *fp, const char *cp, struct rusage *r0, struct rusage *r1, struct timespec *e, struct timespec *b) { long i; time_t t; time_t ms; ms = (e->tv_sec - b->tv_sec) * 100 + (e->tv_nsec - b->tv_nsec) / 10000000; t = (r1->ru_utime.tv_sec - r0->ru_utime.tv_sec) * 100 + (r1->ru_utime.tv_usec - r0->ru_utime.tv_usec) / 10000 + (r1->ru_stime.tv_sec - r0->ru_stime.tv_sec) * 100 + (r1->ru_stime.tv_usec - r0->ru_stime.tv_usec) / 10000; for (; *cp; cp++) if (*cp != '%') (void) fputc(*cp, fp); else if (cp[1]) switch (*++cp) { case 'D': /* (average) unshared data size */ (void)fprintf(fp, "%ld", t == 0 ? 0L : (long)((r1->ru_idrss + r1->ru_isrss - (r0->ru_idrss + r0->ru_isrss)) / t)); break; case 'E': /* elapsed (wall-clock) time */ pcsecs(fp, (long) ms); break; case 'F': /* page faults */ (void)fprintf(fp, "%ld", r1->ru_majflt - r0->ru_majflt); break; case 'I': /* FS blocks in */ (void)fprintf(fp, "%ld", r1->ru_inblock - r0->ru_inblock); break; case 'K': /* (average) total data memory used */ (void)fprintf(fp, "%ld", t == 0 ? 0L : (long)(((r1->ru_ixrss + r1->ru_isrss + r1->ru_idrss) - (r0->ru_ixrss + r0->ru_idrss + r0->ru_isrss)) / t)); break; case 'M': /* max. Resident Set Size */ (void)fprintf(fp, "%ld", r1->ru_maxrss / 2L); break; case 'O': /* FS blocks out */ (void)fprintf(fp, "%ld", r1->ru_oublock - r0->ru_oublock); break; case 'P': /* percent time spent running */ /* check if it did not run at all */ if (ms == 0) { (void)fputs("0.0%", fp); } else { char pb[32]; (void)fputs(strpct(pb, sizeof(pb), (uintmax_t)t, (uintmax_t)ms, 1), fp); (void)fputc('%', fp); } break; case 'R': /* page reclaims */ (void)fprintf(fp, "%ld", r1->ru_minflt - r0->ru_minflt); break; case 'S': /* system CPU time used */ pdeltat(fp, &r1->ru_stime, &r0->ru_stime); break; case 'U': /* user CPU time used */ pdeltat(fp, &r1->ru_utime, &r0->ru_utime); break; case 'W': /* number of swaps */ i = r1->ru_nswap - r0->ru_nswap; (void)fprintf(fp, "%ld", i); break; case 'X': /* (average) shared text size */ (void)fprintf(fp, "%ld", t == 0 ? 0L : (long)((r1->ru_ixrss - r0->ru_ixrss) / t)); break; case 'c': /* num. involuntary context switches */ (void)fprintf(fp, "%ld", r1->ru_nivcsw - r0->ru_nivcsw); break; case 'k': /* number of signals received */ (void)fprintf(fp, "%ld", r1->ru_nsignals-r0->ru_nsignals); break; case 'r': /* socket messages received */ (void)fprintf(fp, "%ld", r1->ru_msgrcv - r0->ru_msgrcv); break; case 's': /* socket messages sent */ (void)fprintf(fp, "%ld", r1->ru_msgsnd - r0->ru_msgsnd); break; case 'w': /* num. voluntary context switches (waits) */ (void)fprintf(fp, "%ld", r1->ru_nvcsw - r0->ru_nvcsw); break; } (void)fputc('\n', fp); } static void pdeltat(FILE *fp, struct timeval *t1, struct timeval *t0) { struct timeval td; timersub(t1, t0, &td); (void)fprintf(fp, "%ld.%01ld", (long)td.tv_sec, (long)(td.tv_usec / 100000)); } #define P2DIG(fp, i) (void)fprintf(fp, "%ld%ld", (i) / 10, (i) % 10) static void pcsecs(FILE *fp, long l) /* PWP: print mm:ss.dd, l is in sec*100 */ { long i; i = l / 360000; if (i) { (void)fprintf(fp, "%ld:", i); i = (l % 360000) / 100; P2DIG(fp, i / 60); goto minsec; } i = l / 100; (void)fprintf(fp, "%ld", i / 60); minsec: i %= 60; (void)fputc(':', fp); P2DIG(fp, i); (void)fputc('.', fp); P2DIG(fp, (l % 100)); }