11590Srgrimes/* 21590Srgrimes * Copyright (c) 1987, 1988, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#ifndef lint 3128203Scharnierstatic const char copyright[] = 321590Srgrimes"@(#) Copyright (c) 1987, 1988, 1993\n\ 331590Srgrimes The Regents of the University of California. All rights reserved.\n"; 341590Srgrimes#endif /* not lint */ 351590Srgrimes 361590Srgrimes#ifndef lint 3728203Scharnier#if 0 381590Srgrimesstatic char sccsid[] = "@(#)time.c 8.1 (Berkeley) 6/6/93"; 3928203Scharnier#endif 4028203Scharnierstatic const char rcsid[] = 4150477Speter "$FreeBSD$"; 421590Srgrimes#endif /* not lint */ 431590Srgrimes 441590Srgrimes#include <sys/types.h> 451590Srgrimes#include <sys/resource.h> 461590Srgrimes#include <sys/signal.h> 4717351Sjdp#include <sys/sysctl.h> 48120747Syar#include <sys/time.h> 4918889Sjkh#include <sys/wait.h> 5017351Sjdp 5117351Sjdp#include <err.h> 5244640Sroberto#include <errno.h> 5372338Sache#include <locale.h> 54120747Syar#include <signal.h> 55120747Syar#include <stdio.h> 56120747Syar#include <stdlib.h> 57169346Sdwmalone#include <stdint.h> 58200462Sdelphij#include <string.h> 5928203Scharnier#include <unistd.h> 601590Srgrimes 6192922Simpstatic int getstathz(void); 6292922Simpstatic void humantime(FILE *, long, long); 63158560Spjdstatic void showtime(FILE *, struct timeval *, struct timeval *, 64158560Spjd struct rusage *); 65158560Spjdstatic void siginfo(int); 6692922Simpstatic void usage(void); 6717351Sjdp 6872338Sachestatic char decimal_point; 69169346Sdwmalonestatic struct timeval before_tv; 70158560Spjdstatic int hflag, pflag; 7172338Sache 7228203Scharnierint 73102944Sdwmalonemain(int argc, char **argv) 741590Srgrimes{ 75158560Spjd int aflag, ch, lflag, status; 76120747Syar int exitonsig; 77120747Syar pid_t pid; 78120744Syar struct rlimit rl; 791590Srgrimes struct rusage ru; 80158560Spjd struct timeval after; 81120747Syar char *ofn = NULL; 8237888Sdes FILE *out = stderr; 831590Srgrimes 8472338Sache (void) setlocale(LC_NUMERIC, ""); 8572338Sache decimal_point = localeconv()->decimal_point[0]; 8672338Sache 8767813Sobrien aflag = hflag = lflag = pflag = 0; 8867813Sobrien while ((ch = getopt(argc, argv, "ahlo:p")) != -1) 891590Srgrimes switch((char)ch) { 9037855Sphk case 'a': 9137888Sdes aflag = 1; 9237855Sphk break; 9367813Sobrien case 'h': 9467813Sobrien hflag = 1; 9567813Sobrien break; 9637913Sdes case 'l': 9737913Sdes lflag = 1; 9837913Sdes break; 9937888Sdes case 'o': 10037888Sdes ofn = optarg; 10137855Sphk break; 10244640Sroberto case 'p': 10344640Sroberto pflag = 1; 10444640Sroberto break; 1051590Srgrimes case '?': 1061590Srgrimes default: 10728203Scharnier usage(); 1081590Srgrimes } 1091590Srgrimes 1101590Srgrimes if (!(argc -= optind)) 1111590Srgrimes exit(0); 1121590Srgrimes argv += optind; 1131590Srgrimes 11437888Sdes if (ofn) { 115244034Sjilles if ((out = fopen(ofn, aflag ? "ae" : "we")) == NULL) 11637913Sdes err(1, "%s", ofn); 11737913Sdes setvbuf(out, (char *)NULL, _IONBF, (size_t)0); 11837888Sdes } 11937855Sphk 120239991Sed (void)gettimeofday(&before_tv, NULL); 12140301Sdes switch(pid = fork()) { 1221590Srgrimes case -1: /* error */ 12328203Scharnier err(1, "time"); 1241590Srgrimes /* NOTREACHED */ 1251590Srgrimes case 0: /* child */ 1261590Srgrimes execvp(*argv, argv); 12797268Stjr err(errno == ENOENT ? 127 : 126, "%s", *argv); 1281590Srgrimes /* NOTREACHED */ 1291590Srgrimes } 1301590Srgrimes /* parent */ 1311590Srgrimes (void)signal(SIGINT, SIG_IGN); 1321590Srgrimes (void)signal(SIGQUIT, SIG_IGN); 133158560Spjd (void)signal(SIGINFO, siginfo); 134121153Sseanc while (wait4(pid, &status, 0, &ru) != pid); 135239991Sed (void)gettimeofday(&after, NULL); 13638520Scracauer if ( ! WIFEXITED(status)) 13728203Scharnier warnx("command terminated abnormally"); 138120747Syar exitonsig = WIFSIGNALED(status) ? WTERMSIG(status) : 0; 139169346Sdwmalone showtime(out, &before_tv, &after, &ru); 1401590Srgrimes if (lflag) { 14117351Sjdp int hz = getstathz(); 14211873Spst u_long ticks; 1431590Srgrimes 1441590Srgrimes ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) + 1451590Srgrimes hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000; 14611873Spst 14711873Spst /* 14811873Spst * If our round-off on the tick calculation still puts us at 0, 14911873Spst * then always assume at least one tick. 15011873Spst */ 15111873Spst if (ticks == 0) 15211873Spst ticks = 1; 15311873Spst 15437855Sphk fprintf(out, "%10ld %s\n", 1551590Srgrimes ru.ru_maxrss, "maximum resident set size"); 15637855Sphk fprintf(out, "%10ld %s\n", 1571590Srgrimes ru.ru_ixrss / ticks, "average shared memory size"); 15837855Sphk fprintf(out, "%10ld %s\n", 1591590Srgrimes ru.ru_idrss / ticks, "average unshared data size"); 16037855Sphk fprintf(out, "%10ld %s\n", 1611590Srgrimes ru.ru_isrss / ticks, "average unshared stack size"); 16237855Sphk fprintf(out, "%10ld %s\n", 1631590Srgrimes ru.ru_minflt, "page reclaims"); 16437855Sphk fprintf(out, "%10ld %s\n", 1651590Srgrimes ru.ru_majflt, "page faults"); 16637855Sphk fprintf(out, "%10ld %s\n", 1671590Srgrimes ru.ru_nswap, "swaps"); 16837855Sphk fprintf(out, "%10ld %s\n", 1691590Srgrimes ru.ru_inblock, "block input operations"); 17037855Sphk fprintf(out, "%10ld %s\n", 1711590Srgrimes ru.ru_oublock, "block output operations"); 17237855Sphk fprintf(out, "%10ld %s\n", 1731590Srgrimes ru.ru_msgsnd, "messages sent"); 17437855Sphk fprintf(out, "%10ld %s\n", 1751590Srgrimes ru.ru_msgrcv, "messages received"); 17637855Sphk fprintf(out, "%10ld %s\n", 1771590Srgrimes ru.ru_nsignals, "signals received"); 17837855Sphk fprintf(out, "%10ld %s\n", 1791590Srgrimes ru.ru_nvcsw, "voluntary context switches"); 18037855Sphk fprintf(out, "%10ld %s\n", 1811590Srgrimes ru.ru_nivcsw, "involuntary context switches"); 1821590Srgrimes } 183120747Syar /* 184120747Syar * If the child has exited on a signal, exit on the same 185120747Syar * signal, too, in order to reproduce the child's exit status. 186120747Syar * However, avoid actually dumping core from the current process. 187120747Syar */ 18838520Scracauer if (exitonsig) { 18987300Sdwmalone if (signal(exitonsig, SIG_DFL) == SIG_ERR) 190120747Syar warn("signal"); 191120744Syar else { 192120744Syar rl.rlim_max = rl.rlim_cur = 0; 193120744Syar if (setrlimit(RLIMIT_CORE, &rl) == -1) 194120744Syar warn("setrlimit"); 19538520Scracauer kill(getpid(), exitonsig); 196120744Syar } 19738520Scracauer } 19818889Sjkh exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); 1991590Srgrimes} 20017351Sjdp 20128203Scharnierstatic void 202102944Sdwmaloneusage(void) 20328203Scharnier{ 20498476Stjr fprintf(stderr, 205146466Sru "usage: time [-al] [-h | -p] [-o file] utility [argument ...]\n"); 20637913Sdes exit(1); 20728203Scharnier} 20828203Scharnier 20917351Sjdp/* 21017351Sjdp * Return the frequency of the kernel's statistics clock. 21117351Sjdp */ 21217351Sjdpstatic int 213102944Sdwmalonegetstathz(void) 21417351Sjdp{ 21517351Sjdp int mib[2]; 21617351Sjdp size_t size; 217120747Syar struct clockinfo clockrate; 21817351Sjdp 21917351Sjdp mib[0] = CTL_KERN; 22017351Sjdp mib[1] = KERN_CLOCKRATE; 22117351Sjdp size = sizeof clockrate; 22217351Sjdp if (sysctl(mib, 2, &clockrate, &size, NULL, 0) == -1) 22317351Sjdp err(1, "sysctl kern.clockrate"); 22417351Sjdp return clockrate.stathz; 22517351Sjdp} 22667813Sobrien 22767813Sobrienstatic void 228102944Sdwmalonehumantime(FILE *out, long sec, long usec) 22967813Sobrien{ 23067813Sobrien long days, hrs, mins; 23167813Sobrien 23267813Sobrien days = sec / (60L * 60 * 24); 23367813Sobrien sec %= (60L * 60 * 24); 23467813Sobrien hrs = sec / (60L * 60); 23567813Sobrien sec %= (60L * 60); 23667813Sobrien mins = sec / 60; 23767813Sobrien sec %= 60; 23867813Sobrien 23967813Sobrien fprintf(out, "\t"); 24067813Sobrien if (days) 24167813Sobrien fprintf(out, "%ldd", days); 24267813Sobrien if (hrs) 24367813Sobrien fprintf(out, "%ldh", hrs); 24467813Sobrien if (mins) 24567813Sobrien fprintf(out, "%ldm", mins); 24672338Sache fprintf(out, "%ld%c%02lds", sec, decimal_point, usec); 24767813Sobrien} 248158560Spjd 249158560Spjdstatic void 250158560Spjdshowtime(FILE *out, struct timeval *before, struct timeval *after, 251158560Spjd struct rusage *ru) 252158560Spjd{ 253158560Spjd 254158560Spjd after->tv_sec -= before->tv_sec; 255158560Spjd after->tv_usec -= before->tv_usec; 256158560Spjd if (after->tv_usec < 0) 257158560Spjd after->tv_sec--, after->tv_usec += 1000000; 258158560Spjd 259158560Spjd if (pflag) { 260158560Spjd /* POSIX wants output that must look like 261158560Spjd "real %f\nuser %f\nsys %f\n" and requires 262158560Spjd at least two digits after the radix. */ 263169346Sdwmalone fprintf(out, "real %jd%c%02ld\n", 264169346Sdwmalone (intmax_t)after->tv_sec, decimal_point, 265158560Spjd after->tv_usec/10000); 266169346Sdwmalone fprintf(out, "user %jd%c%02ld\n", 267169346Sdwmalone (intmax_t)ru->ru_utime.tv_sec, decimal_point, 268158560Spjd ru->ru_utime.tv_usec/10000); 269169346Sdwmalone fprintf(out, "sys %jd%c%02ld\n", 270169346Sdwmalone (intmax_t)ru->ru_stime.tv_sec, decimal_point, 271158560Spjd ru->ru_stime.tv_usec/10000); 272158560Spjd } else if (hflag) { 273158560Spjd humantime(out, after->tv_sec, after->tv_usec/10000); 274158560Spjd fprintf(out, " real\t"); 275158560Spjd humantime(out, ru->ru_utime.tv_sec, ru->ru_utime.tv_usec/10000); 276158560Spjd fprintf(out, " user\t"); 277158560Spjd humantime(out, ru->ru_stime.tv_sec, ru->ru_stime.tv_usec/10000); 278158560Spjd fprintf(out, " sys\n"); 279158560Spjd } else { 280169346Sdwmalone fprintf(out, "%9jd%c%02ld real ", 281169346Sdwmalone (intmax_t)after->tv_sec, decimal_point, 282158560Spjd after->tv_usec/10000); 283169346Sdwmalone fprintf(out, "%9jd%c%02ld user ", 284169346Sdwmalone (intmax_t)ru->ru_utime.tv_sec, decimal_point, 285158560Spjd ru->ru_utime.tv_usec/10000); 286169346Sdwmalone fprintf(out, "%9jd%c%02ld sys\n", 287169346Sdwmalone (intmax_t)ru->ru_stime.tv_sec, decimal_point, 288158560Spjd ru->ru_stime.tv_usec/10000); 289158560Spjd } 290158560Spjd} 291158560Spjd 292158560Spjdstatic void 293158560Spjdsiginfo(int sig __unused) 294158560Spjd{ 295158560Spjd struct timeval after; 296158560Spjd struct rusage ru; 297158560Spjd 298239991Sed (void)gettimeofday(&after, NULL); 299158560Spjd getrusage(RUSAGE_CHILDREN, &ru); 300169346Sdwmalone showtime(stdout, &before_tv, &after, &ru); 301158560Spjd} 302