blob: 677ca6d8bc679a0aafa98182680f0ee96225d3c2 [file] [log] [blame]
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +00001/* vi: set sw=4 ts=4: */
Eric Andersenc3657422001-11-30 07:54:32 +00002/* `time' utility to display resource usage of processes.
3 Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
4
Rob Landleye9a7a622006-09-22 02:52:41 +00005 Licensed under GPL version 2, see file LICENSE in this tarball for details.
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +00006*/
Eric Andersenc3657422001-11-30 07:54:32 +00007/* Originally written by David Keppel <pardo@cs.washington.edu>.
Eric Andersenc7bda1c2004-03-15 08:29:22 +00008 Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
Eric Andersenc3657422001-11-30 07:54:32 +00009 Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000010*/
Eric Andersenc3657422001-11-30 07:54:32 +000011
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000012#include "libbb.h"
Eric Andersenc3657422001-11-30 07:54:32 +000013
Eric Andersenc3657422001-11-30 07:54:32 +000014/* Information on the resources used by a child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000015typedef struct {
16 int waitstatus;
17 struct rusage ru;
Denis Vlasenko459be352007-06-17 19:09:05 +000018 unsigned elapsed_ms; /* Wallclock time of process. */
Eric Andersenc3657422001-11-30 07:54:32 +000019} resource_t;
20
21/* msec = milliseconds = 1/1,000 (1*10e-3) second.
22 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
23
Eric Andersenc3657422001-11-30 07:54:32 +000024#define UL unsigned long
25
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000026static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
Eric Andersenc3657422001-11-30 07:54:32 +000027
28/* The output format for the -p option .*/
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000029static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
Eric Andersenc3657422001-11-30 07:54:32 +000030
31
32/* Format string for printing all statistics verbosely.
33 Keep this output to 24 lines so users on terminals can see it all.*/
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000034static const char long_format[] ALIGN1 =
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000035 "\tCommand being timed: \"%C\"\n"
36 "\tUser time (seconds): %U\n"
37 "\tSystem time (seconds): %S\n"
38 "\tPercent of CPU this job got: %P\n"
39 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
40 "\tAverage shared text size (kbytes): %X\n"
41 "\tAverage unshared data size (kbytes): %D\n"
42 "\tAverage stack size (kbytes): %p\n"
43 "\tAverage total size (kbytes): %K\n"
44 "\tMaximum resident set size (kbytes): %M\n"
45 "\tAverage resident set size (kbytes): %t\n"
46 "\tMajor (requiring I/O) page faults: %F\n"
47 "\tMinor (reclaiming a frame) page faults: %R\n"
48 "\tVoluntary context switches: %w\n"
49 "\tInvoluntary context switches: %c\n"
50 "\tSwaps: %W\n"
51 "\tFile system inputs: %I\n"
52 "\tFile system outputs: %O\n"
53 "\tSocket messages sent: %s\n"
54 "\tSocket messages received: %r\n"
55 "\tSignals delivered: %k\n"
Denis Vlasenkof93ab472006-12-22 12:36:13 +000056 "\tPage size (bytes): %Z\n"
57 "\tExit status: %x";
Eric Andersenc3657422001-11-30 07:54:32 +000058
59
Denis Vlasenkof93ab472006-12-22 12:36:13 +000060/* Wait for and fill in data on child process PID.
61 Return 0 on error, 1 if ok. */
Eric Andersenc3657422001-11-30 07:54:32 +000062
63/* pid_t is short on BSDI, so don't try to promote it. */
Denis Vlasenko25591c32008-02-16 22:58:56 +000064static int resuse_end(pid_t pid, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +000065{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000066 int status;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000067 pid_t caught;
Eric Andersenc3657422001-11-30 07:54:32 +000068
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000069 /* Ignore signals, but don't ignore the children. When wait3
70 returns the child process, set the time the command finished. */
71 while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
Denis Vlasenko25591c32008-02-16 22:58:56 +000072 if (caught == -1 && errno != EINTR)
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000073 return 0;
74 }
Denis Vlasenko459be352007-06-17 19:09:05 +000075 resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000076 resp->waitstatus = status;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000077 return 1;
Eric Andersenc3657422001-11-30 07:54:32 +000078}
79
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +000080/* Print ARGV, with each entry in ARGV separated by FILLER. */
81static void printargv(char *const *argv, const char *filler)
Eric Andersenc3657422001-11-30 07:54:32 +000082{
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +000083 fputs(*argv, stdout);
84 while (*++argv) {
85 fputs(filler, stdout);
86 fputs(*argv, stdout);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000087 }
Eric Andersenc3657422001-11-30 07:54:32 +000088}
89
90/* Return the number of kilobytes corresponding to a number of pages PAGES.
91 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
92
93 Try to do arithmetic so that the risk of overflow errors is minimized.
94 This is funky since the pagesize could be less than 1K.
95 Note: Some machines express getrusage statistics in terms of K,
96 others in terms of pages. */
97
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000098static unsigned long ptok(unsigned long pages)
Eric Andersenc3657422001-11-30 07:54:32 +000099{
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000100 static unsigned long ps;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000101 unsigned long tmp;
Eric Andersenc3657422001-11-30 07:54:32 +0000102
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000103 /* Initialization. */
104 if (ps == 0)
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000105 ps = getpagesize();
Eric Andersenc3657422001-11-30 07:54:32 +0000106
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000107 /* Conversion. */
108 if (pages > (LONG_MAX / ps)) { /* Could overflow. */
109 tmp = pages / 1024; /* Smaller first, */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000110 return tmp * ps; /* then larger. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000111 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000112 /* Could underflow. */
113 tmp = pages * ps; /* Larger first, */
114 return tmp / 1024; /* then smaller. */
Eric Andersenc3657422001-11-30 07:54:32 +0000115}
116
117/* summarize: Report on the system use of a command.
118
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000119 Print the FMT argument except that `%' sequences
Eric Andersenc3657422001-11-30 07:54:32 +0000120 have special meaning, and `\n' and `\t' are translated into
121 newline and tab, respectively, and `\\' is translated into `\'.
122
123 The character following a `%' can be:
124 (* means the tcsh time builtin also recognizes it)
125 % == a literal `%'
126 C == command name and arguments
127* D == average unshared data size in K (ru_idrss+ru_isrss)
128* E == elapsed real (wall clock) time in [hour:]min:sec
129* F == major page faults (required physical I/O) (ru_majflt)
130* I == file system inputs (ru_inblock)
131* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
132* M == maximum resident set size in K (ru_maxrss)
133* O == file system outputs (ru_oublock)
134* P == percent of CPU this job got (total cpu time / elapsed time)
135* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
136* S == system (kernel) time (seconds) (ru_stime)
137* T == system time in [hour:]min:sec
138* U == user time (seconds) (ru_utime)
139* u == user time in [hour:]min:sec
140* W == times swapped out (ru_nswap)
141* X == average amount of shared text in K (ru_ixrss)
142 Z == page size
143* c == involuntary context switches (ru_nivcsw)
144 e == elapsed real time in seconds
145* k == signals delivered (ru_nsignals)
146 p == average unshared stack size in K (ru_isrss)
147* r == socket messages received (ru_msgrcv)
148* s == socket messages sent (ru_msgsnd)
149 t == average resident set size in K (ru_idrss)
150* w == voluntary context switches (ru_nvcsw)
151 x == exit status of command
152
153 Various memory usages are found by converting from page-seconds
154 to kbytes by multiplying by the page size, dividing by 1024,
155 and dividing by elapsed real time.
156
Eric Andersenc3657422001-11-30 07:54:32 +0000157 FMT is the format string, interpreted as described above.
158 COMMAND is the command and args that are being summarized.
159 RESP is resource information on the command. */
160
Denis Vlasenko459be352007-06-17 19:09:05 +0000161#ifndef TICKS_PER_SEC
162#define TICKS_PER_SEC 100
163#endif
164
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000165static void summarize(const char *fmt, char **command, resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000166{
Denis Vlasenko459be352007-06-17 19:09:05 +0000167 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
168 unsigned cpu_ticks; /* Same, in "CPU ticks" */
Eric Andersenc3657422001-11-30 07:54:32 +0000169
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000170 if (WIFSTOPPED(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000171 printf("Command stopped by signal %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000172 WSTOPSIG(resp->waitstatus));
173 else if (WIFSIGNALED(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000174 printf("Command terminated by signal %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000175 WTERMSIG(resp->waitstatus));
176 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000177 printf("Command exited with non-zero status %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000178 WEXITSTATUS(resp->waitstatus));
Eric Andersenc3657422001-11-30 07:54:32 +0000179
Denis Vlasenko459be352007-06-17 19:09:05 +0000180 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
181 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
Eric Andersenc3657422001-11-30 07:54:32 +0000182
Denis Vlasenkobd7bb292007-06-17 23:40:26 +0000183#if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
184 /* 1000 is exactly divisible by TICKS_PER_SEC */
185 cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
186#else
187 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
188#endif
Denis Vlasenko459be352007-06-17 19:09:05 +0000189 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
Eric Andersenc3657422001-11-30 07:54:32 +0000190
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000191 while (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000192 /* Handle leading literal part */
193 int n = strcspn(fmt, "%\\");
194 if (n) {
195 printf("%.*s", n, fmt);
196 fmt += n;
197 continue;
198 }
199
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000200 switch (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000201#ifdef NOT_NEEDED
202 /* Handle literal char */
203 /* Usually we optimize for size, but there is a limit
204 * for everything. With this we do a lot of 1-byte writes */
205 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000206 bb_putchar(*fmt);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000207 break;
208#endif
209
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000210 case '%':
211 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000212#ifdef NOT_NEEDED_YET
213 /* Our format strings do not have these */
214 /* and we do not take format str from user */
215 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000216 bb_putchar('%');
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000217 /*FALLTHROUGH*/
218 case '%':
219 if (!*fmt) goto ret;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000220 bb_putchar(*fmt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000221 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000222#endif
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000223 case 'C': /* The command that got timed. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000224 printargv(command, " ");
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000225 break;
226 case 'D': /* Average unshared data size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000227 printf("%lu",
Denis Vlasenko459be352007-06-17 19:09:05 +0000228 ptok((UL) resp->ru.ru_idrss) / cpu_ticks +
229 ptok((UL) resp->ru.ru_isrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000230 break;
Denis Vlasenko459be352007-06-17 19:09:05 +0000231 case 'E': { /* Elapsed real (wall clock) time. */
232 unsigned seconds = resp->elapsed_ms / 1000;
233 if (seconds >= 3600) /* One hour -> h:m:s. */
234 printf("%uh %um %02us",
235 seconds / 3600,
236 (seconds % 3600) / 60,
237 seconds % 60);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000238 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000239 printf("%um %u.%02us", /* -> m:s. */
240 seconds / 60,
241 seconds % 60,
242 (unsigned)(resp->elapsed_ms / 10) % 100);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000243 break;
Denis Vlasenko459be352007-06-17 19:09:05 +0000244 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000245 case 'F': /* Major page faults. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000246 printf("%lu", resp->ru.ru_majflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000247 break;
248 case 'I': /* Inputs. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000249 printf("%lu", resp->ru.ru_inblock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000250 break;
251 case 'K': /* Average mem usage == data+stack+text. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000252 printf("%lu",
Denis Vlasenko459be352007-06-17 19:09:05 +0000253 ptok((UL) resp->ru.ru_idrss) / cpu_ticks +
254 ptok((UL) resp->ru.ru_isrss) / cpu_ticks +
255 ptok((UL) resp->ru.ru_ixrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000256 break;
257 case 'M': /* Maximum resident set size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000258 printf("%lu", ptok((UL) resp->ru.ru_maxrss));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000259 break;
260 case 'O': /* Outputs. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000261 printf("%lu", resp->ru.ru_oublock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000262 break;
263 case 'P': /* Percent of CPU this job got. */
264 /* % cpu is (total cpu time)/(elapsed time). */
Denis Vlasenko459be352007-06-17 19:09:05 +0000265 if (resp->elapsed_ms > 0)
266 printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000267 else
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000268 printf("?%%");
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000269 break;
270 case 'R': /* Minor page faults (reclaims). */
Denis Vlasenko459be352007-06-17 19:09:05 +0000271 printf("%lu", resp->ru.ru_minflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000272 break;
273 case 'S': /* System time. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000274 printf("%u.%02u",
275 (unsigned)resp->ru.ru_stime.tv_sec,
276 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000277 break;
278 case 'T': /* System time. */
279 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000280 printf("%uh %um %02us",
281 (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
282 (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
283 (unsigned)(resp->ru.ru_stime.tv_sec % 60));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000284 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000285 printf("%um %u.%02us", /* -> m:s. */
286 (unsigned)(resp->ru.ru_stime.tv_sec / 60),
287 (unsigned)(resp->ru.ru_stime.tv_sec % 60),
288 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000289 break;
290 case 'U': /* User time. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000291 printf("%u.%02u",
292 (unsigned)resp->ru.ru_utime.tv_sec,
293 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000294 break;
295 case 'u': /* User time. */
296 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000297 printf("%uh %um %02us",
298 (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
299 (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
300 (unsigned)(resp->ru.ru_utime.tv_sec % 60));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000301 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000302 printf("%um %u.%02us", /* -> m:s. */
303 (unsigned)(resp->ru.ru_utime.tv_sec / 60),
304 (unsigned)(resp->ru.ru_utime.tv_sec % 60),
305 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000306 break;
307 case 'W': /* Times swapped out. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000308 printf("%lu", resp->ru.ru_nswap);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000309 break;
310 case 'X': /* Average shared text size. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000311 printf("%lu", ptok((UL) resp->ru.ru_ixrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000312 break;
313 case 'Z': /* Page size. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000314 printf("%u", getpagesize());
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000315 break;
316 case 'c': /* Involuntary context switches. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000317 printf("%lu", resp->ru.ru_nivcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000318 break;
319 case 'e': /* Elapsed real time in seconds. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000320 printf("%u.%02u",
321 (unsigned)resp->elapsed_ms / 1000,
322 (unsigned)(resp->elapsed_ms / 10) % 100);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000323 break;
324 case 'k': /* Signals delivered. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000325 printf("%lu", resp->ru.ru_nsignals);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000326 break;
327 case 'p': /* Average stack segment. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000328 printf("%lu", ptok((UL) resp->ru.ru_isrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000329 break;
330 case 'r': /* Incoming socket messages received. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000331 printf("%lu", resp->ru.ru_msgrcv);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000332 break;
333 case 's': /* Outgoing socket messages sent. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000334 printf("%lu", resp->ru.ru_msgsnd);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000335 break;
336 case 't': /* Average resident set size. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000337 printf("%lu", ptok((UL) resp->ru.ru_idrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000338 break;
339 case 'w': /* Voluntary context switches. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000340 printf("%lu", resp->ru.ru_nvcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000341 break;
342 case 'x': /* Exit status. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000343 printf("%u", WEXITSTATUS(resp->waitstatus));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000344 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000345 }
Eric Andersenc3657422001-11-30 07:54:32 +0000346 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000347
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000348#ifdef NOT_NEEDED_YET
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000349 case '\\': /* Format escape. */
350 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000351 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000352 bb_putchar('\\');
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000353 /*FALLTHROUGH*/
354 case '\\':
355 if (!*fmt) goto ret;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000356 bb_putchar(*fmt);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000357 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000358 case 't':
Denis Vlasenko4daad902007-09-27 10:20:47 +0000359 bb_putchar('\t');
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000360 break;
361 case 'n':
Denis Vlasenko4daad902007-09-27 10:20:47 +0000362 bb_putchar('\n');
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000363 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000364 }
Eric Andersenc3657422001-11-30 07:54:32 +0000365 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000366#endif
Eric Andersenc3657422001-11-30 07:54:32 +0000367 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000368 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000369 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000370 /* ret: */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000371 bb_putchar('\n');
Eric Andersenc3657422001-11-30 07:54:32 +0000372}
373
374/* Run command CMD and return statistics on it.
375 Put the statistics in *RESP. */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000376static void run_command(char *const *cmd, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000377{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000378 pid_t pid; /* Pid of child. */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000379 void (*interrupt_signal)(int);
380 void (*quit_signal)(int);
Eric Andersenc3657422001-11-30 07:54:32 +0000381
Denis Vlasenko459be352007-06-17 19:09:05 +0000382 resp->elapsed_ms = monotonic_us() / 1000;
Mike Frysinger8e640a12006-06-06 06:08:34 +0000383 pid = vfork(); /* Run CMD as child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000384 if (pid < 0)
385 bb_error_msg_and_die("cannot fork");
Denis Vlasenko25591c32008-02-16 22:58:56 +0000386 if (pid == 0) { /* If child. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000387 /* Don't cast execvp arguments; that causes errors on some systems,
388 versus merely warnings if the cast is left off. */
Denis Vlasenko1d76f432007-02-06 01:20:12 +0000389 BB_EXECVP(cmd[0], cmd);
Denis Vlasenko25591c32008-02-16 22:58:56 +0000390 xfunc_error_retval = (errno == ENOENT ? 127 : 126);
391 bb_error_msg_and_die("cannot run %s", cmd[0]);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000392 }
Eric Andersenc3657422001-11-30 07:54:32 +0000393
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000394 /* Have signals kill the child but not self (if possible). */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000395//TODO: just block all sigs? and reenable them in the very end in main?
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000396 interrupt_signal = signal(SIGINT, SIG_IGN);
397 quit_signal = signal(SIGQUIT, SIG_IGN);
Eric Andersenc3657422001-11-30 07:54:32 +0000398
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000399 if (resuse_end(pid, resp) == 0)
400 bb_error_msg("error waiting for child process");
Eric Andersenc3657422001-11-30 07:54:32 +0000401
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000402 /* Re-enable signals. */
403 signal(SIGINT, interrupt_signal);
404 signal(SIGQUIT, quit_signal);
Eric Andersenc3657422001-11-30 07:54:32 +0000405}
406
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000407int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000408int time_main(int argc, char **argv)
Eric Andersenc3657422001-11-30 07:54:32 +0000409{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000410 resource_t res;
411 const char *output_format = default_format;
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000412 char c;
Eric Andersenc3657422001-11-30 07:54:32 +0000413
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000414 goto next;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000415 /* Parse any options -- don't use getopt() here so we don't
416 * consume the args of our client application... */
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000417 while (argc > 0 && argv[0][0] == '-') {
418 while ((c = *++*argv)) {
419 switch (c) {
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000420 case 'v':
421 output_format = long_format;
422 break;
423 case 'p':
424 output_format = posix_format;
425 break;
426 default:
427 bb_show_usage();
428 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000429 }
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000430 next:
431 argv++;
432 argc--;
433 if (!argc)
434 bb_show_usage();
Eric Andersenc3657422001-11-30 07:54:32 +0000435 }
Eric Andersenc3657422001-11-30 07:54:32 +0000436
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000437 run_command(argv, &res);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000438
439 /* Cheat. printf's are shorter :) */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000440 /* (but see bb_putchar() body for additional wrinkle!) */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000441 stdout = stderr;
442 dup2(2, 1); /* just in case libc does something silly :( */
443 summarize(output_format, argv, &res);
Eric Andersenc3657422001-11-30 07:54:32 +0000444
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000445 if (WIFSTOPPED(res.waitstatus))
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000446 return WSTOPSIG(res.waitstatus);
447 if (WIFSIGNALED(res.waitstatus))
448 return WTERMSIG(res.waitstatus);
449 if (WIFEXITED(res.waitstatus))
450 return WEXITSTATUS(res.waitstatus);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000451 fflush_stdout_and_exit(0);
Eric Andersenc3657422001-11-30 07:54:32 +0000452}