blob: 19b0b44c91c0ba406d0f06135257cb5f4abc0f98 [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
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02005 Licensed under GPLv2, see file LICENSE in this source tree.
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
Pere Orga5bc8c002011-04-11 03:29:49 +020012//usage:#define time_trivial_usage
13//usage: "[-v] PROG ARGS"
14//usage:#define time_full_usage "\n\n"
15//usage: "Run PROG, display resource usage when it exits\n"
Pere Orga5bc8c002011-04-11 03:29:49 +020016//usage: "\n -v Verbose"
17
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000018#include "libbb.h"
Tanguy Pruvot823694d2012-11-18 13:20:29 +010019#include <sys/resource.h> /* getrusage */
Eric Andersenc3657422001-11-30 07:54:32 +000020
Eric Andersenc3657422001-11-30 07:54:32 +000021/* Information on the resources used by a child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000022typedef struct {
23 int waitstatus;
24 struct rusage ru;
Denis Vlasenko459be352007-06-17 19:09:05 +000025 unsigned elapsed_ms; /* Wallclock time of process. */
Eric Andersenc3657422001-11-30 07:54:32 +000026} resource_t;
27
28/* msec = milliseconds = 1/1,000 (1*10e-3) second.
29 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
30
Eric Andersenc3657422001-11-30 07:54:32 +000031#define UL unsigned long
32
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000033static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
Eric Andersenc3657422001-11-30 07:54:32 +000034
35/* The output format for the -p option .*/
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000036static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
Eric Andersenc3657422001-11-30 07:54:32 +000037
Eric Andersenc3657422001-11-30 07:54:32 +000038/* Format string for printing all statistics verbosely.
39 Keep this output to 24 lines so users on terminals can see it all.*/
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000040static const char long_format[] ALIGN1 =
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000041 "\tCommand being timed: \"%C\"\n"
42 "\tUser time (seconds): %U\n"
43 "\tSystem time (seconds): %S\n"
44 "\tPercent of CPU this job got: %P\n"
45 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
46 "\tAverage shared text size (kbytes): %X\n"
47 "\tAverage unshared data size (kbytes): %D\n"
48 "\tAverage stack size (kbytes): %p\n"
49 "\tAverage total size (kbytes): %K\n"
50 "\tMaximum resident set size (kbytes): %M\n"
51 "\tAverage resident set size (kbytes): %t\n"
52 "\tMajor (requiring I/O) page faults: %F\n"
53 "\tMinor (reclaiming a frame) page faults: %R\n"
54 "\tVoluntary context switches: %w\n"
55 "\tInvoluntary context switches: %c\n"
56 "\tSwaps: %W\n"
57 "\tFile system inputs: %I\n"
58 "\tFile system outputs: %O\n"
59 "\tSocket messages sent: %s\n"
60 "\tSocket messages received: %r\n"
61 "\tSignals delivered: %k\n"
Denis Vlasenkof93ab472006-12-22 12:36:13 +000062 "\tPage size (bytes): %Z\n"
63 "\tExit status: %x";
Eric Andersenc3657422001-11-30 07:54:32 +000064
Denis Vlasenkof93ab472006-12-22 12:36:13 +000065/* Wait for and fill in data on child process PID.
66 Return 0 on error, 1 if ok. */
Eric Andersenc3657422001-11-30 07:54:32 +000067/* pid_t is short on BSDI, so don't try to promote it. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +000068static void resuse_end(pid_t pid, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +000069{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000070 pid_t caught;
Eric Andersenc3657422001-11-30 07:54:32 +000071
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000072 /* Ignore signals, but don't ignore the children. When wait3
Tanguy Pruvot823694d2012-11-18 13:20:29 +010073 * returns the child process, set the time the command finished. */
maxwen27116ba2015-08-14 21:41:28 +020074 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
Denis Vlasenkob44c7902008-03-17 09:29:43 +000075 if (caught == -1 && errno != EINTR) {
76 bb_perror_msg("wait");
77 return;
78 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000079 }
Denys Vlasenkof2c8aa62010-01-12 12:52:30 +010080 resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
Eric Andersenc3657422001-11-30 07:54:32 +000081}
82
Denis Vlasenkob44c7902008-03-17 09:29:43 +000083static void printargv(char *const *argv)
Eric Andersenc3657422001-11-30 07:54:32 +000084{
Denis Vlasenkob44c7902008-03-17 09:29:43 +000085 const char *fmt = " %s" + 1;
86 do {
87 printf(fmt, *argv);
88 fmt = " %s";
89 } while (*++argv);
Eric Andersenc3657422001-11-30 07:54:32 +000090}
91
92/* Return the number of kilobytes corresponding to a number of pages PAGES.
93 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
94
95 Try to do arithmetic so that the risk of overflow errors is minimized.
96 This is funky since the pagesize could be less than 1K.
97 Note: Some machines express getrusage statistics in terms of K,
98 others in terms of pages. */
Bernhard Reutner-Fischer0adf7f22009-02-23 16:51:25 +000099static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
Eric Andersenc3657422001-11-30 07:54:32 +0000100{
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 /* Conversion. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000104 if (pages > (LONG_MAX / pagesize)) { /* Could overflow. */
105 tmp = pages / 1024; /* Smaller first, */
106 return tmp * pagesize; /* then larger. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000107 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000108 /* Could underflow. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000109 tmp = pages * pagesize; /* Larger first, */
110 return tmp / 1024; /* then smaller. */
Eric Andersenc3657422001-11-30 07:54:32 +0000111}
112
113/* summarize: Report on the system use of a command.
114
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000115 Print the FMT argument except that `%' sequences
Eric Andersenc3657422001-11-30 07:54:32 +0000116 have special meaning, and `\n' and `\t' are translated into
117 newline and tab, respectively, and `\\' is translated into `\'.
118
119 The character following a `%' can be:
120 (* means the tcsh time builtin also recognizes it)
121 % == a literal `%'
122 C == command name and arguments
123* D == average unshared data size in K (ru_idrss+ru_isrss)
124* E == elapsed real (wall clock) time in [hour:]min:sec
125* F == major page faults (required physical I/O) (ru_majflt)
126* I == file system inputs (ru_inblock)
127* K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
128* M == maximum resident set size in K (ru_maxrss)
129* O == file system outputs (ru_oublock)
130* P == percent of CPU this job got (total cpu time / elapsed time)
131* R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
132* S == system (kernel) time (seconds) (ru_stime)
133* T == system time in [hour:]min:sec
134* U == user time (seconds) (ru_utime)
135* u == user time in [hour:]min:sec
136* W == times swapped out (ru_nswap)
137* X == average amount of shared text in K (ru_ixrss)
138 Z == page size
139* c == involuntary context switches (ru_nivcsw)
140 e == elapsed real time in seconds
141* k == signals delivered (ru_nsignals)
142 p == average unshared stack size in K (ru_isrss)
143* r == socket messages received (ru_msgrcv)
144* s == socket messages sent (ru_msgsnd)
145 t == average resident set size in K (ru_idrss)
146* w == voluntary context switches (ru_nvcsw)
147 x == exit status of command
148
149 Various memory usages are found by converting from page-seconds
150 to kbytes by multiplying by the page size, dividing by 1024,
151 and dividing by elapsed real time.
152
Eric Andersenc3657422001-11-30 07:54:32 +0000153 FMT is the format string, interpreted as described above.
154 COMMAND is the command and args that are being summarized.
155 RESP is resource information on the command. */
156
Denis Vlasenko459be352007-06-17 19:09:05 +0000157#ifndef TICKS_PER_SEC
158#define TICKS_PER_SEC 100
159#endif
160
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000161static void summarize(const char *fmt, char **command, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000162{
Denis Vlasenko459be352007-06-17 19:09:05 +0000163 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
164 unsigned cpu_ticks; /* Same, in "CPU ticks" */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000165 unsigned pagesize = getpagesize();
Eric Andersenc3657422001-11-30 07:54:32 +0000166
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000167 /* Impossible: we do not use WUNTRACED flag in wait()...
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000168 if (WIFSTOPPED(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000169 printf("Command stopped by signal %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000170 WSTOPSIG(resp->waitstatus));
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000171 else */
172 if (WIFSIGNALED(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000173 printf("Command terminated by signal %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000174 WTERMSIG(resp->waitstatus));
175 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000176 printf("Command exited with non-zero status %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000177 WEXITSTATUS(resp->waitstatus));
Eric Andersenc3657422001-11-30 07:54:32 +0000178
Denis Vlasenko459be352007-06-17 19:09:05 +0000179 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
180 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
Eric Andersenc3657422001-11-30 07:54:32 +0000181
Denis Vlasenkobd7bb292007-06-17 23:40:26 +0000182#if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000183 /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
Denis Vlasenkobd7bb292007-06-17 23:40:26 +0000184 cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
185#else
186 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
187#endif
Denis Vlasenko459be352007-06-17 19:09:05 +0000188 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
Eric Andersenc3657422001-11-30 07:54:32 +0000189
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000190 while (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000191 /* Handle leading literal part */
192 int n = strcspn(fmt, "%\\");
193 if (n) {
194 printf("%.*s", n, fmt);
195 fmt += n;
196 continue;
197 }
198
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000199 switch (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000200#ifdef NOT_NEEDED
201 /* Handle literal char */
202 /* Usually we optimize for size, but there is a limit
203 * for everything. With this we do a lot of 1-byte writes */
204 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000205 bb_putchar(*fmt);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000206 break;
207#endif
208
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000209 case '%':
210 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000211#ifdef NOT_NEEDED_YET
212 /* Our format strings do not have these */
213 /* and we do not take format str from user */
214 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000215 bb_putchar('%');
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000216 /*FALLTHROUGH*/
217 case '%':
218 if (!*fmt) goto ret;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000219 bb_putchar(*fmt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000220 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000221#endif
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000222 case 'C': /* The command that got timed. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000223 printargv(command);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000224 break;
225 case 'D': /* Average unshared data size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000226 printf("%lu",
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000227 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
228 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000229 break;
Denis Vlasenko459be352007-06-17 19:09:05 +0000230 case 'E': { /* Elapsed real (wall clock) time. */
231 unsigned seconds = resp->elapsed_ms / 1000;
232 if (seconds >= 3600) /* One hour -> h:m:s. */
233 printf("%uh %um %02us",
234 seconds / 3600,
235 (seconds % 3600) / 60,
236 seconds % 60);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000237 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000238 printf("%um %u.%02us", /* -> m:s. */
239 seconds / 60,
240 seconds % 60,
241 (unsigned)(resp->elapsed_ms / 10) % 100);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000242 break;
Denis Vlasenko459be352007-06-17 19:09:05 +0000243 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000244 case 'F': /* Major page faults. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000245 printf("%lu", resp->ru.ru_majflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000246 break;
247 case 'I': /* Inputs. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000248 printf("%lu", resp->ru.ru_inblock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000249 break;
250 case 'K': /* Average mem usage == data+stack+text. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000251 printf("%lu",
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000252 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
253 ptok(pagesize, (UL) resp->ru.ru_isrss) +
254 ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000255 break;
256 case 'M': /* Maximum resident set size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000257 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000258 break;
259 case 'O': /* Outputs. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000260 printf("%lu", resp->ru.ru_oublock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000261 break;
262 case 'P': /* Percent of CPU this job got. */
263 /* % cpu is (total cpu time)/(elapsed time). */
Denis Vlasenko459be352007-06-17 19:09:05 +0000264 if (resp->elapsed_ms > 0)
265 printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000266 else
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000267 printf("?%%");
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000268 break;
269 case 'R': /* Minor page faults (reclaims). */
Denis Vlasenko459be352007-06-17 19:09:05 +0000270 printf("%lu", resp->ru.ru_minflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000271 break;
272 case 'S': /* System time. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000273 printf("%u.%02u",
274 (unsigned)resp->ru.ru_stime.tv_sec,
275 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000276 break;
277 case 'T': /* System time. */
278 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000279 printf("%uh %um %02us",
280 (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
281 (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
282 (unsigned)(resp->ru.ru_stime.tv_sec % 60));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000283 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000284 printf("%um %u.%02us", /* -> m:s. */
285 (unsigned)(resp->ru.ru_stime.tv_sec / 60),
286 (unsigned)(resp->ru.ru_stime.tv_sec % 60),
287 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000288 break;
289 case 'U': /* User time. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000290 printf("%u.%02u",
291 (unsigned)resp->ru.ru_utime.tv_sec,
292 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000293 break;
294 case 'u': /* User time. */
295 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000296 printf("%uh %um %02us",
297 (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
298 (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
299 (unsigned)(resp->ru.ru_utime.tv_sec % 60));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000300 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000301 printf("%um %u.%02us", /* -> m:s. */
302 (unsigned)(resp->ru.ru_utime.tv_sec / 60),
303 (unsigned)(resp->ru.ru_utime.tv_sec % 60),
304 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000305 break;
306 case 'W': /* Times swapped out. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000307 printf("%lu", resp->ru.ru_nswap);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000308 break;
309 case 'X': /* Average shared text size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000310 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000311 break;
312 case 'Z': /* Page size. */
Bernhard Reutner-Fischer0adf7f22009-02-23 16:51:25 +0000313 printf("%u", pagesize);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000314 break;
315 case 'c': /* Involuntary context switches. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000316 printf("%lu", resp->ru.ru_nivcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000317 break;
318 case 'e': /* Elapsed real time in seconds. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000319 printf("%u.%02u",
320 (unsigned)resp->elapsed_ms / 1000,
321 (unsigned)(resp->elapsed_ms / 10) % 100);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000322 break;
323 case 'k': /* Signals delivered. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000324 printf("%lu", resp->ru.ru_nsignals);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000325 break;
326 case 'p': /* Average stack segment. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000327 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000328 break;
329 case 'r': /* Incoming socket messages received. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000330 printf("%lu", resp->ru.ru_msgrcv);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000331 break;
332 case 's': /* Outgoing socket messages sent. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000333 printf("%lu", resp->ru.ru_msgsnd);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000334 break;
335 case 't': /* Average resident set size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000336 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000337 break;
338 case 'w': /* Voluntary context switches. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000339 printf("%lu", resp->ru.ru_nvcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000340 break;
341 case 'x': /* Exit status. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000342 printf("%u", WEXITSTATUS(resp->waitstatus));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000343 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000344 }
Eric Andersenc3657422001-11-30 07:54:32 +0000345 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000346
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000347#ifdef NOT_NEEDED_YET
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000348 case '\\': /* Format escape. */
349 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000350 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000351 bb_putchar('\\');
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000352 /*FALLTHROUGH*/
353 case '\\':
354 if (!*fmt) goto ret;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000355 bb_putchar(*fmt);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000356 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000357 case 't':
Denis Vlasenko4daad902007-09-27 10:20:47 +0000358 bb_putchar('\t');
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000359 break;
360 case 'n':
Denis Vlasenko4daad902007-09-27 10:20:47 +0000361 bb_putchar('\n');
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000362 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000363 }
Eric Andersenc3657422001-11-30 07:54:32 +0000364 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000365#endif
Eric Andersenc3657422001-11-30 07:54:32 +0000366 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000367 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000368 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000369 /* ret: */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000370 bb_putchar('\n');
Eric Andersenc3657422001-11-30 07:54:32 +0000371}
372
373/* Run command CMD and return statistics on it.
374 Put the statistics in *RESP. */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000375static void run_command(char *const *cmd, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000376{
Pascal Bellard21e8e8d2010-07-04 00:57:03 +0200377 pid_t pid;
Denis Vlasenko25591c32008-02-16 22:58:56 +0000378 void (*interrupt_signal)(int);
379 void (*quit_signal)(int);
Eric Andersenc3657422001-11-30 07:54:32 +0000380
Denys Vlasenkof2c8aa62010-01-12 12:52:30 +0100381 resp->elapsed_ms = monotonic_ms();
Pascal Bellard926031b2010-07-04 15:32:38 +0200382 pid = xvfork();
Pascal Bellard21e8e8d2010-07-04 00:57:03 +0200383 if (pid == 0) {
384 /* Child */
385 BB_EXECVP_or_die((char**)cmd);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000386 }
Eric Andersenc3657422001-11-30 07:54:32 +0000387
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000388 /* Have signals kill the child but not self (if possible). */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000389//TODO: just block all sigs? and reenable them in the very end in main?
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000390 interrupt_signal = signal(SIGINT, SIG_IGN);
391 quit_signal = signal(SIGQUIT, SIG_IGN);
Eric Andersenc3657422001-11-30 07:54:32 +0000392
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000393 resuse_end(pid, resp);
Eric Andersenc3657422001-11-30 07:54:32 +0000394
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000395 /* Re-enable signals. */
396 signal(SIGINT, interrupt_signal);
397 signal(SIGQUIT, quit_signal);
Eric Andersenc3657422001-11-30 07:54:32 +0000398}
399
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000400int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000401int time_main(int argc UNUSED_PARAM, char **argv)
Eric Andersenc3657422001-11-30 07:54:32 +0000402{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000403 resource_t res;
404 const char *output_format = default_format;
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000405 int opt;
Eric Andersenc3657422001-11-30 07:54:32 +0000406
Denis Vlasenko94884eb2008-07-11 15:05:51 +0000407 opt_complementary = "-1"; /* at least one arg */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000408 /* "+": stop on first non-option */
409 opt = getopt32(argv, "+vp");
410 argv += optind;
411 if (opt & 1)
412 output_format = long_format;
413 if (opt & 2)
414 output_format = posix_format;
Eric Andersenc3657422001-11-30 07:54:32 +0000415
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000416 run_command(argv, &res);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000417
418 /* Cheat. printf's are shorter :) */
Denys Vlasenkoc0664722010-01-02 18:49:22 +0100419 xdup2(STDERR_FILENO, STDOUT_FILENO);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000420 summarize(output_format, argv, &res);
Eric Andersenc3657422001-11-30 07:54:32 +0000421
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000422 if (WIFSTOPPED(res.waitstatus))
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000423 return WSTOPSIG(res.waitstatus);
424 if (WIFSIGNALED(res.waitstatus))
425 return WTERMSIG(res.waitstatus);
426 if (WIFEXITED(res.waitstatus))
427 return WEXITSTATUS(res.waitstatus);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000428 fflush_stdout_and_exit(EXIT_SUCCESS);
Eric Andersenc3657422001-11-30 07:54:32 +0000429}