1 /* 2 * Copyright (c) 1987, 1988, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 * @(#) Copyright (c) 1987, 1988, 1993 The Regents of the University of California. All rights reserved. 30 * @(#)time.c 8.1 (Berkeley) 6/6/93 31 * $FreeBSD: src/usr.bin/time/time.c,v 1.14.2.5 2002/06/28 08:35:15 tjr Exp $ 32 */ 33 34 #include <sys/time.h> 35 #include <sys/resource.h> 36 #include <sys/signal.h> 37 #include <sys/wait.h> 38 39 #include <err.h> 40 #include <errno.h> 41 #ifndef BOOTSTRAPPING 42 #include <kinfo.h> 43 #endif 44 #include <locale.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <stdint.h> 49 #include <string.h> 50 #include <time.h> 51 #include <unistd.h> 52 53 static void humantime(FILE *, long, long); 54 static void showtime(FILE *, struct timespec *, struct timespec *, 55 struct rusage *); 56 static void siginfo(int); 57 58 static void usage(void); 59 60 static sig_atomic_t siginfo_recvd; 61 static char decimal_point; 62 struct timespec before_ts; 63 static int hflag, pflag; 64 65 int 66 main(int argc, char **argv) 67 { 68 pid_t pid; 69 int aflag, ch, lflag, status; 70 int exit_on_sig; 71 struct timespec after; 72 struct rusage ru; 73 FILE *out = stderr; 74 const char *ofn = NULL; 75 76 setlocale(LC_NUMERIC, ""); 77 decimal_point = localeconv()->decimal_point[0]; 78 79 aflag = hflag = lflag = pflag = 0; 80 while ((ch = getopt(argc, argv, "ahlo:p")) != -1) 81 switch (ch) { 82 case 'a': 83 aflag = 1; 84 break; 85 case 'h': 86 hflag = 1; 87 break; 88 case 'l': 89 #ifndef BOOTSTRAPPING 90 lflag = 1; 91 #endif 92 break; 93 case 'o': 94 ofn = optarg; 95 break; 96 case 'p': 97 pflag = 1; 98 break; 99 default: 100 usage(); 101 } 102 103 if (!(argc -= optind)) 104 exit(0); 105 argv += optind; 106 107 if (ofn) { 108 if ((out = fopen(ofn, aflag ? "ae" : "we")) == NULL) 109 err(1, "%s", ofn); 110 setvbuf(out, NULL, _IONBF, (size_t)0); 111 } 112 113 if (clock_gettime(CLOCK_MONOTONIC, &before_ts) == -1) 114 err(1, "clock_gettime failed"); 115 switch (pid = fork()) { 116 case -1: /* error */ 117 err(1, "could not fork"); 118 /* NOTREACHED */ 119 case 0: /* child */ 120 execvp(*argv, argv); 121 err(errno == ENOENT ? 127 : 126, "%s", *argv); 122 /* NOTREACHED */ 123 } 124 /* parent */ 125 if (signal(SIGINT, SIG_IGN) == SIG_ERR) 126 err(1, "signal failed"); 127 if (signal(SIGQUIT, SIG_IGN) == SIG_ERR) 128 err(1, "signal failed"); 129 siginfo_recvd = 0; 130 #ifdef SIGINFO 131 if (signal(SIGINFO, siginfo) == SIG_ERR) 132 err(1, "signal failed"); 133 siginterrupt(SIGINFO,1 ); 134 #endif 135 while (wait4(pid, &status, 0, &ru) != pid){ 136 if (siginfo_recvd) { 137 siginfo_recvd = 0; 138 if (clock_gettime(CLOCK_MONOTONIC, &after) == -1) 139 err(1, "clock_gettime failed"); 140 getrusage(RUSAGE_CHILDREN, &ru); 141 showtime(stdout, &before_ts, &after, &ru); 142 } 143 } 144 if (clock_gettime(CLOCK_MONOTONIC, &after) == -1) 145 err(1, "clock_gettime failed"); 146 if (!WIFEXITED(status)) 147 warnx("command terminated abnormally"); 148 exit_on_sig = WIFSIGNALED(status) ? WTERMSIG(status) : 0; 149 showtime(out, &before_ts, &after, &ru); 150 #ifndef BOOTSTRAPPING 151 if (lflag) { 152 int hz; 153 u_long ticks; 154 155 if (kinfo_get_sched_stathz(&hz)) 156 err(1, "kinfo_get_sched_stathz"); 157 ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) + 158 hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000; 159 160 /* 161 * If our round-off on the tick calculation still puts us at 0, 162 * then always assume at least one tick. 163 */ 164 if (ticks == 0) 165 ticks = 1; 166 167 fprintf(out, "%10ld %s\n", 168 ru.ru_maxrss, "maximum resident set size"); 169 fprintf(out, "%10ld %s\n", 170 ru.ru_ixrss / ticks, "average shared memory size"); 171 fprintf(out, "%10ld %s\n", 172 ru.ru_idrss / ticks, "average unshared data size"); 173 fprintf(out, "%10ld %s\n", 174 ru.ru_isrss / ticks, "average unshared stack size"); 175 fprintf(out, "%10ld %s\n", 176 ru.ru_minflt, "page reclaims"); 177 fprintf(out, "%10ld %s\n", 178 ru.ru_majflt, "page faults"); 179 fprintf(out, "%10ld %s\n", 180 ru.ru_nswap, "swaps"); 181 fprintf(out, "%10ld %s\n", 182 ru.ru_inblock, "block input operations"); 183 fprintf(out, "%10ld %s\n", 184 ru.ru_oublock, "block output operations"); 185 fprintf(out, "%10ld %s\n", 186 ru.ru_msgsnd, "messages sent"); 187 fprintf(out, "%10ld %s\n", 188 ru.ru_msgrcv, "messages received"); 189 fprintf(out, "%10ld %s\n", 190 ru.ru_nsignals, "signals received"); 191 fprintf(out, "%10ld %s\n", 192 ru.ru_nvcsw, "voluntary context switches"); 193 fprintf(out, "%10ld %s\n", 194 ru.ru_nivcsw, "involuntary context switches"); 195 } 196 #endif 197 /* 198 * If the child has exited on a signal, exit on the same 199 * signal, too, in order to reproduce the child's exit 200 * status. However, avoid actually dumping core from 201 * current process. 202 */ 203 if (exit_on_sig) { 204 if (signal(exit_on_sig, SIG_DFL) == SIG_ERR) 205 warn("signal failed"); 206 else 207 kill(getpid(), exit_on_sig); 208 } 209 exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); 210 } 211 212 static void 213 usage(void) 214 { 215 fprintf(stderr, 216 "usage: time [-al] [-h|-p] [-o file] utility [argument ...]\n"); 217 exit(1); 218 } 219 220 static void 221 humantime(FILE *out, long sec, long usec) 222 { 223 long days, hrs, mins; 224 225 days = sec / (60L * 60 * 24); 226 sec %= (60L * 60 * 24); 227 hrs = sec / (60L * 60); 228 sec %= (60L * 60); 229 mins = sec / 60; 230 sec %= 60; 231 232 fprintf(out, "\t"); 233 if (days) 234 fprintf(out, "%ldd", days); 235 if (hrs) 236 fprintf(out, "%ldh", hrs); 237 if (mins) 238 fprintf(out, "%ldm", mins); 239 fprintf(out, "%ld%c%02lds", sec, decimal_point, usec); 240 } 241 242 static void 243 showtime(FILE *out, struct timespec *before, struct timespec *after, 244 struct rusage *ru) 245 { 246 247 after->tv_sec -= before->tv_sec; 248 after->tv_nsec -= before->tv_nsec; 249 if (after->tv_nsec < 0) 250 after->tv_sec--, after->tv_nsec += 1000000000L; 251 252 if (pflag) { 253 /* POSIX wants output that must look like 254 "real %f\nuser %f\nsys %f\n" and requires 255 at least two digits after the radix. */ 256 fprintf(out, "real %jd%c%02ld\n", 257 (intmax_t)after->tv_sec, decimal_point, 258 after->tv_nsec/10000000L); 259 fprintf(out, "user %jd%c%02ld\n", 260 (intmax_t)ru->ru_utime.tv_sec, decimal_point, 261 ru->ru_utime.tv_usec/10000); 262 fprintf(out, "sys %jd%c%02ld\n", 263 (intmax_t)ru->ru_stime.tv_sec, decimal_point, 264 ru->ru_stime.tv_usec/10000); 265 } else if (hflag) { 266 humantime(out, after->tv_sec, after->tv_nsec/10000000); 267 fprintf(out, " real\t"); 268 humantime(out, ru->ru_utime.tv_sec, ru->ru_utime.tv_usec/10000); 269 fprintf(out, " user\t"); 270 humantime(out, ru->ru_stime.tv_sec, ru->ru_stime.tv_usec/10000); 271 fprintf(out, " sys\n"); 272 } else { 273 fprintf(out, "%9jd%c%02ld real ", 274 (intmax_t)after->tv_sec, decimal_point, 275 after->tv_nsec/10000000); 276 fprintf(out, "%9jd%c%02ld user ", 277 (intmax_t)ru->ru_utime.tv_sec, decimal_point, 278 ru->ru_utime.tv_usec/10000); 279 fprintf(out, "%9jd%c%02ld sys\n", 280 (intmax_t)ru->ru_stime.tv_sec, decimal_point, 281 ru->ru_stime.tv_usec/10000); 282 } 283 } 284 285 static void 286 siginfo(int sig __unused) 287 { 288 siginfo_recvd = 1; 289 } 290