blob: df10eeb0210795938d3082110cb7d069a2e93ce1 [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
Bernhard Reutner-Fischerc89982d2006-06-03 19:49:21 +000012#include "busybox.h"
Eric Andersenc3657422001-11-30 07:54:32 +000013
14#define TV_MSEC tv_usec / 1000
Eric Andersenc3657422001-11-30 07:54:32 +000015
16/* Information on the resources used by a child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000017typedef struct {
18 int waitstatus;
19 struct rusage ru;
20 struct timeval start, elapsed; /* Wallclock time of process. */
Eric Andersenc3657422001-11-30 07:54:32 +000021} resource_t;
22
23/* msec = milliseconds = 1/1,000 (1*10e-3) second.
24 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
25
26#ifndef TICKS_PER_SEC
27#define TICKS_PER_SEC 100
28#endif
29
30/* The number of milliseconds in one `tick' used by the `rusage' structure. */
31#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
32
33/* Return the number of clock ticks that occur in M milliseconds. */
34#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
35
36#define UL unsigned long
37
38static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T";
39
40/* The output format for the -p option .*/
41static const char *const posix_format = "real %e\nuser %U\nsys %S";
42
43
44/* Format string for printing all statistics verbosely.
45 Keep this output to 24 lines so users on terminals can see it all.*/
46static const char *const long_format =
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000047 "\tCommand being timed: \"%C\"\n"
48 "\tUser time (seconds): %U\n"
49 "\tSystem time (seconds): %S\n"
50 "\tPercent of CPU this job got: %P\n"
51 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
52 "\tAverage shared text size (kbytes): %X\n"
53 "\tAverage unshared data size (kbytes): %D\n"
54 "\tAverage stack size (kbytes): %p\n"
55 "\tAverage total size (kbytes): %K\n"
56 "\tMaximum resident set size (kbytes): %M\n"
57 "\tAverage resident set size (kbytes): %t\n"
58 "\tMajor (requiring I/O) page faults: %F\n"
59 "\tMinor (reclaiming a frame) page faults: %R\n"
60 "\tVoluntary context switches: %w\n"
61 "\tInvoluntary context switches: %c\n"
62 "\tSwaps: %W\n"
63 "\tFile system inputs: %I\n"
64 "\tFile system outputs: %O\n"
65 "\tSocket messages sent: %s\n"
66 "\tSocket messages received: %r\n"
67 "\tSignals delivered: %k\n"
Denis Vlasenkof93ab472006-12-22 12:36:13 +000068 "\tPage size (bytes): %Z\n"
69 "\tExit status: %x";
Eric Andersenc3657422001-11-30 07:54:32 +000070
71
Denis Vlasenkof93ab472006-12-22 12:36:13 +000072/* Wait for and fill in data on child process PID.
73 Return 0 on error, 1 if ok. */
Eric Andersenc3657422001-11-30 07:54:32 +000074
75/* pid_t is short on BSDI, so don't try to promote it. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000076static int resuse_end(pid_t pid, resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +000077{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000078 int status;
Eric Andersenc3657422001-11-30 07:54:32 +000079
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000080 pid_t caught;
Eric Andersenc3657422001-11-30 07:54:32 +000081
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000082 /* Ignore signals, but don't ignore the children. When wait3
83 returns the child process, set the time the command finished. */
84 while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
85 if (caught == -1)
86 return 0;
87 }
Eric Andersenc3657422001-11-30 07:54:32 +000088
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000089 gettimeofday(&resp->elapsed, (struct timezone *) 0);
90 resp->elapsed.tv_sec -= resp->start.tv_sec;
91 if (resp->elapsed.tv_usec < resp->start.tv_usec) {
92 /* Manually carry a one from the seconds field. */
93 resp->elapsed.tv_usec += 1000000;
94 --resp->elapsed.tv_sec;
95 }
96 resp->elapsed.tv_usec -= resp->start.tv_usec;
Eric Andersenc3657422001-11-30 07:54:32 +000097
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000098 resp->waitstatus = status;
Eric Andersenc3657422001-11-30 07:54:32 +000099
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000100 return 1;
Eric Andersenc3657422001-11-30 07:54:32 +0000101}
102
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000103/* Print ARGV, with each entry in ARGV separated by FILLER. */
104static void printargv(char *const *argv, const char *filler)
Eric Andersenc3657422001-11-30 07:54:32 +0000105{
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000106 fputs(*argv, stdout);
107 while (*++argv) {
108 fputs(filler, stdout);
109 fputs(*argv, stdout);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000110 }
Eric Andersenc3657422001-11-30 07:54:32 +0000111}
112
113/* Return the number of kilobytes corresponding to a number of pages PAGES.
114 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
115
116 Try to do arithmetic so that the risk of overflow errors is minimized.
117 This is funky since the pagesize could be less than 1K.
118 Note: Some machines express getrusage statistics in terms of K,
119 others in terms of pages. */
120
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000121static unsigned long ptok(unsigned long pages)
Eric Andersenc3657422001-11-30 07:54:32 +0000122{
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000123 static unsigned long ps;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000124 unsigned long tmp;
Eric Andersenc3657422001-11-30 07:54:32 +0000125
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000126 /* Initialization. */
127 if (ps == 0)
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000128 ps = getpagesize();
Eric Andersenc3657422001-11-30 07:54:32 +0000129
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000130 /* Conversion. */
131 if (pages > (LONG_MAX / ps)) { /* Could overflow. */
132 tmp = pages / 1024; /* Smaller first, */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000133 return tmp * ps; /* then larger. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000134 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000135 /* Could underflow. */
136 tmp = pages * ps; /* Larger first, */
137 return tmp / 1024; /* then smaller. */
Eric Andersenc3657422001-11-30 07:54:32 +0000138}
139
140/* summarize: Report on the system use of a command.
141
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000142 Print the FMT argument except that `%' sequences
Eric Andersenc3657422001-11-30 07:54:32 +0000143 have special meaning, and `\n' and `\t' are translated into
144 newline and tab, respectively, and `\\' is translated into `\'.
145
146 The character following a `%' can be:
147 (* means the tcsh time builtin also recognizes it)
148 % == a literal `%'
149 C == command name and arguments
150* D == average unshared data size in K (ru_idrss+ru_isrss)
151* E == elapsed real (wall clock) time in [hour:]min:sec
152* F == major page faults (required physical I/O) (ru_majflt)
153* I == file system inputs (ru_inblock)
154* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
155* M == maximum resident set size in K (ru_maxrss)
156* O == file system outputs (ru_oublock)
157* P == percent of CPU this job got (total cpu time / elapsed time)
158* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
159* S == system (kernel) time (seconds) (ru_stime)
160* T == system time in [hour:]min:sec
161* U == user time (seconds) (ru_utime)
162* u == user time in [hour:]min:sec
163* W == times swapped out (ru_nswap)
164* X == average amount of shared text in K (ru_ixrss)
165 Z == page size
166* c == involuntary context switches (ru_nivcsw)
167 e == elapsed real time in seconds
168* k == signals delivered (ru_nsignals)
169 p == average unshared stack size in K (ru_isrss)
170* r == socket messages received (ru_msgrcv)
171* s == socket messages sent (ru_msgsnd)
172 t == average resident set size in K (ru_idrss)
173* w == voluntary context switches (ru_nvcsw)
174 x == exit status of command
175
176 Various memory usages are found by converting from page-seconds
177 to kbytes by multiplying by the page size, dividing by 1024,
178 and dividing by elapsed real time.
179
Eric Andersenc3657422001-11-30 07:54:32 +0000180 FMT is the format string, interpreted as described above.
181 COMMAND is the command and args that are being summarized.
182 RESP is resource information on the command. */
183
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000184static void summarize(const char *fmt, char **command, resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000185{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000186 unsigned long r; /* Elapsed real milliseconds. */
187 unsigned long v; /* Elapsed virtual (CPU) milliseconds. */
Eric Andersenc3657422001-11-30 07:54:32 +0000188
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000189 if (WIFSTOPPED(resp->waitstatus))
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000190 printf("Command stopped by signal %d\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000191 WSTOPSIG(resp->waitstatus));
192 else if (WIFSIGNALED(resp->waitstatus))
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000193 printf("Command terminated by signal %d\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000194 WTERMSIG(resp->waitstatus));
195 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000196 printf("Command exited with non-zero status %d\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000197 WEXITSTATUS(resp->waitstatus));
Eric Andersenc3657422001-11-30 07:54:32 +0000198
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000199 /* Convert all times to milliseconds. Occasionally, one of these values
200 comes out as zero. Dividing by zero causes problems, so we first
201 check the time value. If it is zero, then we take `evasive action'
202 instead of calculating a value. */
Eric Andersenc3657422001-11-30 07:54:32 +0000203
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000204 r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
Eric Andersenc3657422001-11-30 07:54:32 +0000205
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000206 v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
207 resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
Eric Andersenc3657422001-11-30 07:54:32 +0000208
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000209 /* putchar() != putc(stdout) in glibc! */
210
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000211 while (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000212 /* Handle leading literal part */
213 int n = strcspn(fmt, "%\\");
214 if (n) {
215 printf("%.*s", n, fmt);
216 fmt += n;
217 continue;
218 }
219
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000220 switch (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000221#ifdef NOT_NEEDED
222 /* Handle literal char */
223 /* Usually we optimize for size, but there is a limit
224 * for everything. With this we do a lot of 1-byte writes */
225 default:
226 putc(*fmt, stdout);
227 break;
228#endif
229
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000230 case '%':
231 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000232#ifdef NOT_NEEDED_YET
233 /* Our format strings do not have these */
234 /* and we do not take format str from user */
235 default:
236 putc('%', stdout);
237 /*FALLTHROUGH*/
238 case '%':
239 if (!*fmt) goto ret;
240 putc(*fmt, stdout);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000241 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000242#endif
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000243 case 'C': /* The command that got timed. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000244 printargv(command, " ");
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000245 break;
246 case 'D': /* Average unshared data size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000247 printf("%lu",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000248 MSEC_TO_TICKS(v) == 0 ? 0 :
249 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
250 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
251 break;
252 case 'E': /* Elapsed real (wall clock) time. */
253 if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000254 printf("%ldh %ldm %02lds",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000255 resp->elapsed.tv_sec / 3600,
256 (resp->elapsed.tv_sec % 3600) / 60,
257 resp->elapsed.tv_sec % 60);
258 else
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000259 printf("%ldm %ld.%02lds", /* -> m:s. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000260 resp->elapsed.tv_sec / 60,
261 resp->elapsed.tv_sec % 60,
262 resp->elapsed.tv_usec / 10000);
263 break;
264 case 'F': /* Major page faults. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000265 printf("%ld", resp->ru.ru_majflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000266 break;
267 case 'I': /* Inputs. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000268 printf("%ld", resp->ru.ru_inblock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000269 break;
270 case 'K': /* Average mem usage == data+stack+text. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000271 printf("%lu",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000272 MSEC_TO_TICKS(v) == 0 ? 0 :
273 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
274 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v) +
275 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
276 break;
277 case 'M': /* Maximum resident set size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000278 printf("%lu", ptok((UL) resp->ru.ru_maxrss));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000279 break;
280 case 'O': /* Outputs. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000281 printf("%ld", resp->ru.ru_oublock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000282 break;
283 case 'P': /* Percent of CPU this job got. */
284 /* % cpu is (total cpu time)/(elapsed time). */
285 if (r > 0)
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000286 printf("%lu%%", (v * 100 / r));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000287 else
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000288 printf("?%%");
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000289 break;
290 case 'R': /* Minor page faults (reclaims). */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000291 printf("%ld", resp->ru.ru_minflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000292 break;
293 case 'S': /* System time. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000294 printf("%ld.%02ld",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000295 resp->ru.ru_stime.tv_sec,
296 resp->ru.ru_stime.TV_MSEC / 10);
297 break;
298 case 'T': /* System time. */
299 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000300 printf("%ldh %ldm %02lds",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000301 resp->ru.ru_stime.tv_sec / 3600,
302 (resp->ru.ru_stime.tv_sec % 3600) / 60,
303 resp->ru.ru_stime.tv_sec % 60);
304 else
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000305 printf("%ldm %ld.%02lds", /* -> m:s. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000306 resp->ru.ru_stime.tv_sec / 60,
307 resp->ru.ru_stime.tv_sec % 60,
308 resp->ru.ru_stime.tv_usec / 10000);
309 break;
310 case 'U': /* User time. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000311 printf("%ld.%02ld",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000312 resp->ru.ru_utime.tv_sec,
313 resp->ru.ru_utime.TV_MSEC / 10);
314 break;
315 case 'u': /* User time. */
316 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000317 printf("%ldh %ldm %02lds",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000318 resp->ru.ru_utime.tv_sec / 3600,
319 (resp->ru.ru_utime.tv_sec % 3600) / 60,
320 resp->ru.ru_utime.tv_sec % 60);
321 else
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000322 printf("%ldm %ld.%02lds", /* -> m:s. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000323 resp->ru.ru_utime.tv_sec / 60,
324 resp->ru.ru_utime.tv_sec % 60,
325 resp->ru.ru_utime.tv_usec / 10000);
326 break;
327 case 'W': /* Times swapped out. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000328 printf("%ld", resp->ru.ru_nswap);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000329 break;
330 case 'X': /* Average shared text size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000331 printf("%lu",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000332 MSEC_TO_TICKS(v) == 0 ? 0 :
333 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
334 break;
335 case 'Z': /* Page size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000336 printf("%d", getpagesize());
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000337 break;
338 case 'c': /* Involuntary context switches. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000339 printf("%ld", resp->ru.ru_nivcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000340 break;
341 case 'e': /* Elapsed real time in seconds. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000342 printf("%ld.%02ld",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000343 resp->elapsed.tv_sec, resp->elapsed.tv_usec / 10000);
344 break;
345 case 'k': /* Signals delivered. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000346 printf("%ld", resp->ru.ru_nsignals);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000347 break;
348 case 'p': /* Average stack segment. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000349 printf("%lu",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000350 MSEC_TO_TICKS(v) == 0 ? 0 :
351 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
352 break;
353 case 'r': /* Incoming socket messages received. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000354 printf("%ld", resp->ru.ru_msgrcv);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000355 break;
356 case 's': /* Outgoing socket messages sent. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000357 printf("%ld", resp->ru.ru_msgsnd);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000358 break;
359 case 't': /* Average resident set size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000360 printf("%lu",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000361 MSEC_TO_TICKS(v) == 0 ? 0 :
362 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v));
363 break;
364 case 'w': /* Voluntary context switches. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000365 printf("%ld", resp->ru.ru_nvcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000366 break;
367 case 'x': /* Exit status. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000368 printf("%d", WEXITSTATUS(resp->waitstatus));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000369 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000370 }
Eric Andersenc3657422001-11-30 07:54:32 +0000371 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000372
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000373#ifdef NOT_NEEDED_YET
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000374 case '\\': /* Format escape. */
375 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000376 default:
377 putc('\\', stdout);
378 /*FALLTHROUGH*/
379 case '\\':
380 if (!*fmt) goto ret;
381 putc(*fmt, stdout);
382 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000383 case 't':
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000384 putc('\t', stdout);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000385 break;
386 case 'n':
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000387 putc('\n', stdout);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000388 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000389 }
Eric Andersenc3657422001-11-30 07:54:32 +0000390 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000391#endif
Eric Andersenc3657422001-11-30 07:54:32 +0000392 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000393 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000394 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000395 /* ret: */
396 putc('\n', stdout);
Eric Andersenc3657422001-11-30 07:54:32 +0000397}
398
399/* Run command CMD and return statistics on it.
400 Put the statistics in *RESP. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000401static void run_command(char *const *cmd, resource_t * resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000402{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000403 pid_t pid; /* Pid of child. */
404 __sighandler_t interrupt_signal, quit_signal;
Eric Andersenc3657422001-11-30 07:54:32 +0000405
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000406 gettimeofday(&resp->start, (struct timezone *) 0);
Mike Frysinger8e640a12006-06-06 06:08:34 +0000407 pid = vfork(); /* Run CMD as child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000408 if (pid < 0)
409 bb_error_msg_and_die("cannot fork");
410 else if (pid == 0) { /* If child. */
411 /* Don't cast execvp arguments; that causes errors on some systems,
412 versus merely warnings if the cast is left off. */
413 execvp(cmd[0], cmd);
414 bb_error_msg("cannot run %s", cmd[0]);
415 _exit(errno == ENOENT ? 127 : 126);
416 }
Eric Andersenc3657422001-11-30 07:54:32 +0000417
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000418 /* Have signals kill the child but not self (if possible). */
419 interrupt_signal = signal(SIGINT, SIG_IGN);
420 quit_signal = signal(SIGQUIT, SIG_IGN);
Eric Andersenc3657422001-11-30 07:54:32 +0000421
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000422 if (resuse_end(pid, resp) == 0)
423 bb_error_msg("error waiting for child process");
Eric Andersenc3657422001-11-30 07:54:32 +0000424
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000425 /* Re-enable signals. */
426 signal(SIGINT, interrupt_signal);
427 signal(SIGQUIT, quit_signal);
Eric Andersenc3657422001-11-30 07:54:32 +0000428}
429
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000430int time_main(int argc, char **argv)
Eric Andersenc3657422001-11-30 07:54:32 +0000431{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000432 resource_t res;
433 const char *output_format = default_format;
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000434 char c;
Eric Andersenc3657422001-11-30 07:54:32 +0000435
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000436 goto next;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000437 /* Parse any options -- don't use getopt() here so we don't
438 * consume the args of our client application... */
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000439 while (argc > 0 && argv[0][0] == '-') {
440 while ((c = *++*argv)) {
441 switch (c) {
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000442 case 'v':
443 output_format = long_format;
444 break;
445 case 'p':
446 output_format = posix_format;
447 break;
448 default:
449 bb_show_usage();
450 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000451 }
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000452 next:
453 argv++;
454 argc--;
455 if (!argc)
456 bb_show_usage();
Eric Andersenc3657422001-11-30 07:54:32 +0000457 }
Eric Andersenc3657422001-11-30 07:54:32 +0000458
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000459 run_command(argv, &res);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000460
461 /* Cheat. printf's are shorter :) */
462 stdout = stderr;
463 dup2(2, 1); /* just in case libc does something silly :( */
464 summarize(output_format, argv, &res);
Eric Andersenc3657422001-11-30 07:54:32 +0000465
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000466 if (WIFSTOPPED(res.waitstatus))
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000467 return WSTOPSIG(res.waitstatus);
468 if (WIFSIGNALED(res.waitstatus))
469 return WTERMSIG(res.waitstatus);
470 if (WIFEXITED(res.waitstatus))
471 return WEXITSTATUS(res.waitstatus);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000472 fflush_stdout_and_exit(0);
Eric Andersenc3657422001-11-30 07:54:32 +0000473}