blob: 9facc36573a2ac9a1d5581ddf766983b00d8aa21 [file] [log] [blame]
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +00001/* vi: set sw=4 ts=4: */
Denis Vlasenkob44c7902008-03-17 09:29:43 +00002/* 'time' utility to display resource usage of processes.
Eric Andersenc3657422001-11-30 07:54:32 +00003 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
Eric Andersenc3657422001-11-30 07:54:32 +000031/* Format string for printing all statistics verbosely.
32 Keep this output to 24 lines so users on terminals can see it all.*/
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000033static const char long_format[] ALIGN1 =
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000034 "\tCommand being timed: \"%C\"\n"
35 "\tUser time (seconds): %U\n"
36 "\tSystem time (seconds): %S\n"
37 "\tPercent of CPU this job got: %P\n"
38 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
39 "\tAverage shared text size (kbytes): %X\n"
40 "\tAverage unshared data size (kbytes): %D\n"
41 "\tAverage stack size (kbytes): %p\n"
42 "\tAverage total size (kbytes): %K\n"
43 "\tMaximum resident set size (kbytes): %M\n"
44 "\tAverage resident set size (kbytes): %t\n"
45 "\tMajor (requiring I/O) page faults: %F\n"
46 "\tMinor (reclaiming a frame) page faults: %R\n"
47 "\tVoluntary context switches: %w\n"
48 "\tInvoluntary context switches: %c\n"
49 "\tSwaps: %W\n"
50 "\tFile system inputs: %I\n"
51 "\tFile system outputs: %O\n"
52 "\tSocket messages sent: %s\n"
53 "\tSocket messages received: %r\n"
54 "\tSignals delivered: %k\n"
Denis Vlasenkof93ab472006-12-22 12:36:13 +000055 "\tPage size (bytes): %Z\n"
56 "\tExit status: %x";
Eric Andersenc3657422001-11-30 07:54:32 +000057
Denis Vlasenkof93ab472006-12-22 12:36:13 +000058/* Wait for and fill in data on child process PID.
59 Return 0 on error, 1 if ok. */
Eric Andersenc3657422001-11-30 07:54:32 +000060/* pid_t is short on BSDI, so don't try to promote it. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +000061static void resuse_end(pid_t pid, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +000062{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000063 pid_t caught;
Eric Andersenc3657422001-11-30 07:54:32 +000064
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000065 /* Ignore signals, but don't ignore the children. When wait3
66 returns the child process, set the time the command finished. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +000067 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
68 if (caught == -1 && errno != EINTR) {
69 bb_perror_msg("wait");
70 return;
71 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000072 }
Denys Vlasenkof2c8aa62010-01-12 12:52:30 +010073 resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
Eric Andersenc3657422001-11-30 07:54:32 +000074}
75
Denis Vlasenkob44c7902008-03-17 09:29:43 +000076static void printargv(char *const *argv)
Eric Andersenc3657422001-11-30 07:54:32 +000077{
Denis Vlasenkob44c7902008-03-17 09:29:43 +000078 const char *fmt = " %s" + 1;
79 do {
80 printf(fmt, *argv);
81 fmt = " %s";
82 } while (*++argv);
Eric Andersenc3657422001-11-30 07:54:32 +000083}
84
85/* Return the number of kilobytes corresponding to a number of pages PAGES.
86 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
87
88 Try to do arithmetic so that the risk of overflow errors is minimized.
89 This is funky since the pagesize could be less than 1K.
90 Note: Some machines express getrusage statistics in terms of K,
91 others in terms of pages. */
Bernhard Reutner-Fischer0adf7f22009-02-23 16:51:25 +000092static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
Eric Andersenc3657422001-11-30 07:54:32 +000093{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000094 unsigned long tmp;
Eric Andersenc3657422001-11-30 07:54:32 +000095
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000096 /* Conversion. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +000097 if (pages > (LONG_MAX / pagesize)) { /* Could overflow. */
98 tmp = pages / 1024; /* Smaller first, */
99 return tmp * pagesize; /* then larger. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000100 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000101 /* Could underflow. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000102 tmp = pages * pagesize; /* Larger first, */
103 return tmp / 1024; /* then smaller. */
Eric Andersenc3657422001-11-30 07:54:32 +0000104}
105
106/* summarize: Report on the system use of a command.
107
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000108 Print the FMT argument except that `%' sequences
Eric Andersenc3657422001-11-30 07:54:32 +0000109 have special meaning, and `\n' and `\t' are translated into
110 newline and tab, respectively, and `\\' is translated into `\'.
111
112 The character following a `%' can be:
113 (* means the tcsh time builtin also recognizes it)
114 % == a literal `%'
115 C == command name and arguments
116* D == average unshared data size in K (ru_idrss+ru_isrss)
117* E == elapsed real (wall clock) time in [hour:]min:sec
118* F == major page faults (required physical I/O) (ru_majflt)
119* I == file system inputs (ru_inblock)
120* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
121* M == maximum resident set size in K (ru_maxrss)
122* O == file system outputs (ru_oublock)
123* P == percent of CPU this job got (total cpu time / elapsed time)
124* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
125* S == system (kernel) time (seconds) (ru_stime)
126* T == system time in [hour:]min:sec
127* U == user time (seconds) (ru_utime)
128* u == user time in [hour:]min:sec
129* W == times swapped out (ru_nswap)
130* X == average amount of shared text in K (ru_ixrss)
131 Z == page size
132* c == involuntary context switches (ru_nivcsw)
133 e == elapsed real time in seconds
134* k == signals delivered (ru_nsignals)
135 p == average unshared stack size in K (ru_isrss)
136* r == socket messages received (ru_msgrcv)
137* s == socket messages sent (ru_msgsnd)
138 t == average resident set size in K (ru_idrss)
139* w == voluntary context switches (ru_nvcsw)
140 x == exit status of command
141
142 Various memory usages are found by converting from page-seconds
143 to kbytes by multiplying by the page size, dividing by 1024,
144 and dividing by elapsed real time.
145
Eric Andersenc3657422001-11-30 07:54:32 +0000146 FMT is the format string, interpreted as described above.
147 COMMAND is the command and args that are being summarized.
148 RESP is resource information on the command. */
149
Denis Vlasenko459be352007-06-17 19:09:05 +0000150#ifndef TICKS_PER_SEC
151#define TICKS_PER_SEC 100
152#endif
153
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000154static void summarize(const char *fmt, char **command, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000155{
Denis Vlasenko459be352007-06-17 19:09:05 +0000156 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
157 unsigned cpu_ticks; /* Same, in "CPU ticks" */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000158 unsigned pagesize = getpagesize();
Eric Andersenc3657422001-11-30 07:54:32 +0000159
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000160 /* Impossible: we do not use WUNTRACED flag in wait()...
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000161 if (WIFSTOPPED(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000162 printf("Command stopped by signal %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000163 WSTOPSIG(resp->waitstatus));
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000164 else */
165 if (WIFSIGNALED(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000166 printf("Command terminated by signal %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000167 WTERMSIG(resp->waitstatus));
168 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000169 printf("Command exited with non-zero status %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000170 WEXITSTATUS(resp->waitstatus));
Eric Andersenc3657422001-11-30 07:54:32 +0000171
Denis Vlasenko459be352007-06-17 19:09:05 +0000172 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
173 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
Eric Andersenc3657422001-11-30 07:54:32 +0000174
Denis Vlasenkobd7bb292007-06-17 23:40:26 +0000175#if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000176 /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
Denis Vlasenkobd7bb292007-06-17 23:40:26 +0000177 cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
178#else
179 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
180#endif
Denis Vlasenko459be352007-06-17 19:09:05 +0000181 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
Eric Andersenc3657422001-11-30 07:54:32 +0000182
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000183 while (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000184 /* Handle leading literal part */
185 int n = strcspn(fmt, "%\\");
186 if (n) {
187 printf("%.*s", n, fmt);
188 fmt += n;
189 continue;
190 }
191
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000192 switch (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000193#ifdef NOT_NEEDED
194 /* Handle literal char */
195 /* Usually we optimize for size, but there is a limit
196 * for everything. With this we do a lot of 1-byte writes */
197 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000198 bb_putchar(*fmt);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000199 break;
200#endif
201
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000202 case '%':
203 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000204#ifdef NOT_NEEDED_YET
205 /* Our format strings do not have these */
206 /* and we do not take format str from user */
207 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000208 bb_putchar('%');
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000209 /*FALLTHROUGH*/
210 case '%':
211 if (!*fmt) goto ret;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000212 bb_putchar(*fmt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000213 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000214#endif
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000215 case 'C': /* The command that got timed. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000216 printargv(command);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000217 break;
218 case 'D': /* Average unshared data size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000219 printf("%lu",
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000220 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
221 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000222 break;
Denis Vlasenko459be352007-06-17 19:09:05 +0000223 case 'E': { /* Elapsed real (wall clock) time. */
224 unsigned seconds = resp->elapsed_ms / 1000;
225 if (seconds >= 3600) /* One hour -> h:m:s. */
226 printf("%uh %um %02us",
227 seconds / 3600,
228 (seconds % 3600) / 60,
229 seconds % 60);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000230 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000231 printf("%um %u.%02us", /* -> m:s. */
232 seconds / 60,
233 seconds % 60,
234 (unsigned)(resp->elapsed_ms / 10) % 100);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000235 break;
Denis Vlasenko459be352007-06-17 19:09:05 +0000236 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000237 case 'F': /* Major page faults. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000238 printf("%lu", resp->ru.ru_majflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000239 break;
240 case 'I': /* Inputs. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000241 printf("%lu", resp->ru.ru_inblock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000242 break;
243 case 'K': /* Average mem usage == data+stack+text. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000244 printf("%lu",
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000245 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
246 ptok(pagesize, (UL) resp->ru.ru_isrss) +
247 ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000248 break;
249 case 'M': /* Maximum resident set size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000250 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000251 break;
252 case 'O': /* Outputs. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000253 printf("%lu", resp->ru.ru_oublock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000254 break;
255 case 'P': /* Percent of CPU this job got. */
256 /* % cpu is (total cpu time)/(elapsed time). */
Denis Vlasenko459be352007-06-17 19:09:05 +0000257 if (resp->elapsed_ms > 0)
258 printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000259 else
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000260 printf("?%%");
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000261 break;
262 case 'R': /* Minor page faults (reclaims). */
Denis Vlasenko459be352007-06-17 19:09:05 +0000263 printf("%lu", resp->ru.ru_minflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000264 break;
265 case 'S': /* System time. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000266 printf("%u.%02u",
267 (unsigned)resp->ru.ru_stime.tv_sec,
268 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000269 break;
270 case 'T': /* System time. */
271 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000272 printf("%uh %um %02us",
273 (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
274 (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
275 (unsigned)(resp->ru.ru_stime.tv_sec % 60));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000276 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000277 printf("%um %u.%02us", /* -> m:s. */
278 (unsigned)(resp->ru.ru_stime.tv_sec / 60),
279 (unsigned)(resp->ru.ru_stime.tv_sec % 60),
280 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000281 break;
282 case 'U': /* User time. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000283 printf("%u.%02u",
284 (unsigned)resp->ru.ru_utime.tv_sec,
285 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000286 break;
287 case 'u': /* User time. */
288 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000289 printf("%uh %um %02us",
290 (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
291 (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
292 (unsigned)(resp->ru.ru_utime.tv_sec % 60));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000293 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000294 printf("%um %u.%02us", /* -> m:s. */
295 (unsigned)(resp->ru.ru_utime.tv_sec / 60),
296 (unsigned)(resp->ru.ru_utime.tv_sec % 60),
297 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000298 break;
299 case 'W': /* Times swapped out. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000300 printf("%lu", resp->ru.ru_nswap);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000301 break;
302 case 'X': /* Average shared text size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000303 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000304 break;
305 case 'Z': /* Page size. */
Bernhard Reutner-Fischer0adf7f22009-02-23 16:51:25 +0000306 printf("%u", pagesize);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000307 break;
308 case 'c': /* Involuntary context switches. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000309 printf("%lu", resp->ru.ru_nivcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000310 break;
311 case 'e': /* Elapsed real time in seconds. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000312 printf("%u.%02u",
313 (unsigned)resp->elapsed_ms / 1000,
314 (unsigned)(resp->elapsed_ms / 10) % 100);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000315 break;
316 case 'k': /* Signals delivered. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000317 printf("%lu", resp->ru.ru_nsignals);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000318 break;
319 case 'p': /* Average stack segment. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000320 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000321 break;
322 case 'r': /* Incoming socket messages received. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000323 printf("%lu", resp->ru.ru_msgrcv);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000324 break;
325 case 's': /* Outgoing socket messages sent. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000326 printf("%lu", resp->ru.ru_msgsnd);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000327 break;
328 case 't': /* Average resident set size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000329 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000330 break;
331 case 'w': /* Voluntary context switches. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000332 printf("%lu", resp->ru.ru_nvcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000333 break;
334 case 'x': /* Exit status. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000335 printf("%u", WEXITSTATUS(resp->waitstatus));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000336 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000337 }
Eric Andersenc3657422001-11-30 07:54:32 +0000338 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000339
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000340#ifdef NOT_NEEDED_YET
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000341 case '\\': /* Format escape. */
342 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000343 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000344 bb_putchar('\\');
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000345 /*FALLTHROUGH*/
346 case '\\':
347 if (!*fmt) goto ret;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000348 bb_putchar(*fmt);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000349 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000350 case 't':
Denis Vlasenko4daad902007-09-27 10:20:47 +0000351 bb_putchar('\t');
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000352 break;
353 case 'n':
Denis Vlasenko4daad902007-09-27 10:20:47 +0000354 bb_putchar('\n');
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000355 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000356 }
Eric Andersenc3657422001-11-30 07:54:32 +0000357 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000358#endif
Eric Andersenc3657422001-11-30 07:54:32 +0000359 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000360 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000361 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000362 /* ret: */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000363 bb_putchar('\n');
Eric Andersenc3657422001-11-30 07:54:32 +0000364}
365
366/* Run command CMD and return statistics on it.
367 Put the statistics in *RESP. */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000368static void run_command(char *const *cmd, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000369{
Pascal Bellard21e8e8d2010-07-04 00:57:03 +0200370 pid_t pid;
Denis Vlasenko25591c32008-02-16 22:58:56 +0000371 void (*interrupt_signal)(int);
372 void (*quit_signal)(int);
Eric Andersenc3657422001-11-30 07:54:32 +0000373
Denys Vlasenkof2c8aa62010-01-12 12:52:30 +0100374 resp->elapsed_ms = monotonic_ms();
Pascal Bellard926031b2010-07-04 15:32:38 +0200375 pid = xvfork();
Pascal Bellard21e8e8d2010-07-04 00:57:03 +0200376 if (pid == 0) {
377 /* Child */
378 BB_EXECVP_or_die((char**)cmd);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000379 }
Eric Andersenc3657422001-11-30 07:54:32 +0000380
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000381 /* Have signals kill the child but not self (if possible). */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000382//TODO: just block all sigs? and reenable them in the very end in main?
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000383 interrupt_signal = signal(SIGINT, SIG_IGN);
384 quit_signal = signal(SIGQUIT, SIG_IGN);
Eric Andersenc3657422001-11-30 07:54:32 +0000385
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000386 resuse_end(pid, resp);
Eric Andersenc3657422001-11-30 07:54:32 +0000387
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000388 /* Re-enable signals. */
389 signal(SIGINT, interrupt_signal);
390 signal(SIGQUIT, quit_signal);
Eric Andersenc3657422001-11-30 07:54:32 +0000391}
392
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000393int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000394int time_main(int argc UNUSED_PARAM, char **argv)
Eric Andersenc3657422001-11-30 07:54:32 +0000395{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000396 resource_t res;
397 const char *output_format = default_format;
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000398 int opt;
Eric Andersenc3657422001-11-30 07:54:32 +0000399
Denis Vlasenko94884eb2008-07-11 15:05:51 +0000400 opt_complementary = "-1"; /* at least one arg */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000401 /* "+": stop on first non-option */
402 opt = getopt32(argv, "+vp");
403 argv += optind;
404 if (opt & 1)
405 output_format = long_format;
406 if (opt & 2)
407 output_format = posix_format;
Eric Andersenc3657422001-11-30 07:54:32 +0000408
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000409 run_command(argv, &res);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000410
411 /* Cheat. printf's are shorter :) */
Denys Vlasenkoc0664722010-01-02 18:49:22 +0100412 xdup2(STDERR_FILENO, STDOUT_FILENO);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000413 summarize(output_format, argv, &res);
Eric Andersenc3657422001-11-30 07:54:32 +0000414
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000415 if (WIFSTOPPED(res.waitstatus))
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000416 return WSTOPSIG(res.waitstatus);
417 if (WIFSIGNALED(res.waitstatus))
418 return WTERMSIG(res.waitstatus);
419 if (WIFEXITED(res.waitstatus))
420 return WEXITSTATUS(res.waitstatus);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000421 fflush_stdout_and_exit(EXIT_SUCCESS);
Eric Andersenc3657422001-11-30 07:54:32 +0000422}