]> git.saurik.com Git - apple/shell_cmds.git/blame - time/time.c
shell_cmds-216.60.1.tar.gz
[apple/shell_cmds.git] / time / time.c
CommitLineData
44bd5ea7
A
1/* $NetBSD: time.c,v 1.9 1997/10/20 03:28:21 lukem Exp $ */
2
3/*
4 * Copyright (c) 1987, 1988, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
47d9aef8
A
36#include <errno.h>
37#include <err.h>
38#include <inttypes.h>
39#include <langinfo.h>
40#include <libproc.h>
41#include <locale.h>
44bd5ea7 42#include <sys/cdefs.h>
47d9aef8 43#include <sysexits.h>
44bd5ea7
A
44#include <sys/types.h>
45#include <sys/time.h>
46#include <sys/resource.h>
47#include <sys/wait.h>
48#include <signal.h>
47d9aef8 49#include <stdbool.h>
44bd5ea7 50#include <stdio.h>
47d9aef8 51#include <stdint.h>
44bd5ea7 52#include <stdlib.h>
47d9aef8 53#include <time.h>
44bd5ea7 54#include <unistd.h>
44bd5ea7
A
55
56int lflag;
57int portableflag;
47d9aef8 58bool child_running = true;
44bd5ea7 59
47d9aef8
A
60void
61child_handler(int sig)
62{
63 child_running = false;
64}
44bd5ea7
A
65
66int
47d9aef8 67main(int argc, char **argv)
44bd5ea7
A
68{
69 int pid;
47d9aef8
A
70 int ch, status, rusage_ret = -1;
71 uint64_t before_ns, after_ns, duration_ns, duration_secs, duration_frac_ns;
44bd5ea7 72 struct rusage ru;
47d9aef8
A
73 struct rusage_info_v4 ruinfo;
74 sigset_t sigmask, suspmask, origmask;
44bd5ea7 75
44bd5ea7 76 lflag = 0;
47d9aef8 77 while ((ch = getopt(argc, argv, "lp")) != -1) {
44bd5ea7
A
78 switch((char)ch) {
79 case 'p':
80 portableflag = 1;
81 break;
82 case 'l':
83 lflag = 1;
84 break;
85 case '?':
86 default:
47d9aef8 87 fprintf(stderr, "usage: time [-lp] <command>\n");
44bd5ea7
A
88 exit(1);
89 }
47d9aef8 90 }
44bd5ea7 91
47d9aef8 92 if (!(argc -= optind)) {
44bd5ea7 93 exit(0);
47d9aef8 94 }
44bd5ea7
A
95 argv += optind;
96
47d9aef8
A
97 sigemptyset(&sigmask);
98 /*
99 * Block SIGCHLD so that the check for `child_running` doesn't miss the
100 * handler before calling `sigsuspend` and blocking forever.
101 */
102 sigaddset(&sigmask, SIGCHLD);
103 sigprocmask(SIG_BLOCK, &sigmask, &origmask);
104
105 /*
106 * Ensure child signals are handled by the parent prior to fork; otherwise,
107 * they could be missed between the child forking and calling `sigsuspend`.
108 */
109 (void)signal(SIGCHLD, child_handler);
110
111 sigemptyset(&suspmask);
112
113 before_ns = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
114 /*
115 * NB: Don't add anything between these two lines -- measurement is
116 * happening now.
117 */
118 switch (pid = vfork()) {
119 case -1: /* error */
120 err(EX_OSERR, "time");
121 __builtin_unreachable();
122 case 0: /* child */
123 /*
124 * Allow the child to respond to signals by resetting to the original
125 * signal handling behavior.
126 */
127 (void)sigprocmask(SIG_SETMASK, &origmask, NULL);
44bd5ea7
A
128 execvp(*argv, argv);
129 perror(*argv);
130 _exit((errno == ENOENT) ? 127 : 126);
47d9aef8
A
131 __builtin_unreachable();
132 default: /* parent */
133 break;
44bd5ea7
A
134 }
135
47d9aef8
A
136 /*
137 * Let the child handle signals that normally exit.
138 */
44bd5ea7
A
139 (void)signal(SIGINT, SIG_IGN);
140 (void)signal(SIGQUIT, SIG_IGN);
47d9aef8
A
141
142 while (child_running) {
143 /*
144 * This would be racy, but SIGCHLD is blocked above (as part of
145 * `sigmask`.
146 */
147 sigsuspend(&suspmask);
148 }
149 /*
150 * NB: Minimize what's added between these statements to preserve the
151 * accuracy of the time measurement.
152 */
153 after_ns = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
154 if (lflag) {
155 rusage_ret = proc_pid_rusage(pid, RUSAGE_INFO_V4, (void **)&ruinfo);
156 }
157 while (wait3(&status, 0, &ru) != pid) {
158 }
159 if (!WIFEXITED(status)) {
44bd5ea7 160 fprintf(stderr, "Command terminated abnormally.\n");
47d9aef8
A
161 }
162 duration_ns = after_ns - before_ns;
163 duration_secs = duration_ns / (1000 * 1000 * 1000);
164 duration_frac_ns = duration_ns - (duration_secs * 1000 * 1000 * 1000);
44bd5ea7
A
165
166 if (portableflag) {
47d9aef8
A
167 char *radix = NULL;
168
254f12f7 169 setlocale(LC_ALL, "");
44bd5ea7 170
254f12f7
A
171 radix = nl_langinfo(RADIXCHAR);
172 if (!radix || radix[0] == '\0') {
173 radix = ".";
174 }
175
47d9aef8
A
176 fprintf(stderr, "real %9" PRIu64 "%s%02" PRIu64 "\n",
177 duration_secs, radix, duration_frac_ns / (10 * 1000 * 1000));
178 fprintf(stderr, "user %9ld%s%02ld\n",
254f12f7 179 (long)ru.ru_utime.tv_sec, radix, (long)ru.ru_utime.tv_usec/10000);
47d9aef8 180 fprintf(stderr, "sys %9ld%s%02ld\n",
254f12f7
A
181 (long)ru.ru_stime.tv_sec, radix, (long)ru.ru_stime.tv_usec/10000);
182 } else {
47d9aef8
A
183 fprintf(stderr, "%9" PRIu64 ".%02" PRIu64 " real ",
184 duration_secs, duration_frac_ns / (10 * 1000 * 1000));
44bd5ea7
A
185 fprintf(stderr, "%9ld.%02ld user ",
186 (long)ru.ru_utime.tv_sec, (long)ru.ru_utime.tv_usec/10000);
187 fprintf(stderr, "%9ld.%02ld sys\n",
188 (long)ru.ru_stime.tv_sec, (long)ru.ru_stime.tv_usec/10000);
189 }
190
191 if (lflag) {
47d9aef8 192 int hz = 100; /* XXX */
44bd5ea7
A
193 long ticks;
194
195 ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) +
196 hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000;
197
47d9aef8 198 fprintf(stderr, "%20ld %s\n",
44bd5ea7 199 ru.ru_maxrss, "maximum resident set size");
47d9aef8 200 fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_ixrss / ticks : 0,
44bd5ea7 201 "average shared memory size");
47d9aef8 202 fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_idrss / ticks : 0,
44bd5ea7 203 "average unshared data size");
47d9aef8 204 fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_isrss / ticks : 0,
44bd5ea7 205 "average unshared stack size");
47d9aef8 206 fprintf(stderr, "%20ld %s\n",
44bd5ea7 207 ru.ru_minflt, "page reclaims");
47d9aef8 208 fprintf(stderr, "%20ld %s\n",
44bd5ea7 209 ru.ru_majflt, "page faults");
47d9aef8 210 fprintf(stderr, "%20ld %s\n",
44bd5ea7 211 ru.ru_nswap, "swaps");
47d9aef8 212 fprintf(stderr, "%20ld %s\n",
44bd5ea7 213 ru.ru_inblock, "block input operations");
47d9aef8 214 fprintf(stderr, "%20ld %s\n",
44bd5ea7 215 ru.ru_oublock, "block output operations");
47d9aef8 216 fprintf(stderr, "%20ld %s\n",
44bd5ea7 217 ru.ru_msgsnd, "messages sent");
47d9aef8 218 fprintf(stderr, "%20ld %s\n",
44bd5ea7 219 ru.ru_msgrcv, "messages received");
47d9aef8 220 fprintf(stderr, "%20ld %s\n",
44bd5ea7 221 ru.ru_nsignals, "signals received");
47d9aef8 222 fprintf(stderr, "%20ld %s\n",
44bd5ea7 223 ru.ru_nvcsw, "voluntary context switches");
47d9aef8 224 fprintf(stderr, "%20ld %s\n",
44bd5ea7 225 ru.ru_nivcsw, "involuntary context switches");
47d9aef8
A
226
227 if (rusage_ret >= 0) {
228 if (ruinfo.ri_instructions > 0) {
229 fprintf(stderr, "%20" PRIu64 " %s\n", ruinfo.ri_instructions,
230 "instructions retired");
231 }
232 if (ruinfo.ri_cycles > 0) {
233 fprintf(stderr, "%20" PRIu64 " %s\n", ruinfo.ri_cycles,
234 "cycles elapsed");
235 }
236 if (ruinfo.ri_lifetime_max_phys_footprint > 0) {
237 fprintf(stderr, "%20" PRIu64 " %s\n",
238 ruinfo.ri_lifetime_max_phys_footprint,
239 "peak memory footprint");
240 }
241 }
44bd5ea7
A
242 }
243
47d9aef8 244 exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
44bd5ea7 245}