blob: c4bcbcbc9ae87bd72c30289d9c5ab4e67cc06afb [file] [log] [blame]
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +00001/* vi: set sw=4 ts=4: */
Denys Vlasenkoebe6d9d2017-10-05 14:40:24 +02002/*
3 * 'time' utility to display resource usage of processes.
4 * Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 */
Eric Andersenc3657422001-11-30 07:54:32 +00008/* Originally written by David Keppel <pardo@cs.washington.edu>.
Denys Vlasenkoebe6d9d2017-10-05 14:40:24 +02009 * Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
10 * Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
11 */
Denys Vlasenkofb4da162016-11-22 23:14:24 +010012//config:config TIME
Denys Vlasenkob097a842018-12-28 03:20:17 +010013//config: bool "time (6.8 kb)"
Denys Vlasenkofb4da162016-11-22 23:14:24 +010014//config: default y
15//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020016//config: The time command runs the specified program with the given arguments.
17//config: When the command finishes, time writes a message to standard output
18//config: giving timing statistics about this program run.
Eric Andersenc3657422001-11-30 07:54:32 +000019
Denys Vlasenkof88e3bf2016-11-22 23:54:17 +010020//applet:IF_TIME(APPLET(time, BB_DIR_USR_BIN, BB_SUID_DROP))
21
22//kbuild:lib-$(CONFIG_TIME) += time.o
23
Pere Orga5bc8c002011-04-11 03:29:49 +020024//usage:#define time_trivial_usage
Tommi Rantala5fe5be22017-04-28 17:54:14 +020025//usage: "[-vpa] [-o FILE] PROG ARGS"
Pere Orga5bc8c002011-04-11 03:29:49 +020026//usage:#define time_full_usage "\n\n"
27//usage: "Run PROG, display resource usage when it exits\n"
Pere Orga5bc8c002011-04-11 03:29:49 +020028//usage: "\n -v Verbose"
Tommi Rantala854174f2017-04-24 19:08:53 +030029//usage: "\n -p POSIX output format"
Denys Vlasenkoa1a3b592017-04-28 18:01:18 +020030//usage: "\n -f FMT Custom format"
Tommi Rantala5fe5be22017-04-28 17:54:14 +020031//usage: "\n -o FILE Write result to FILE"
32//usage: "\n -a Append (else overwrite)"
Pere Orga5bc8c002011-04-11 03:29:49 +020033
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000034#include "libbb.h"
Eric Andersenc3657422001-11-30 07:54:32 +000035
Denys Vlasenkoc0943ac2021-04-14 22:14:36 +020036#ifndef HAVE_WAIT3
37static pid_t wait3(int *status, int options, struct rusage *rusage)
38{
39 return wait4(-1, status, options, rusage);
40}
41#endif
42
Eric Andersenc3657422001-11-30 07:54:32 +000043/* Information on the resources used by a child process. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000044typedef struct {
45 int waitstatus;
46 struct rusage ru;
Denis Vlasenko459be352007-06-17 19:09:05 +000047 unsigned elapsed_ms; /* Wallclock time of process. */
Eric Andersenc3657422001-11-30 07:54:32 +000048} resource_t;
49
50/* msec = milliseconds = 1/1,000 (1*10e-3) second.
51 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
52
Eric Andersenc3657422001-11-30 07:54:32 +000053#define UL unsigned long
54
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000055static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
Eric Andersenc3657422001-11-30 07:54:32 +000056
57/* The output format for the -p option .*/
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000058static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
Eric Andersenc3657422001-11-30 07:54:32 +000059
Eric Andersenc3657422001-11-30 07:54:32 +000060/* Format string for printing all statistics verbosely.
61 Keep this output to 24 lines so users on terminals can see it all.*/
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000062static const char long_format[] ALIGN1 =
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000063 "\tCommand being timed: \"%C\"\n"
64 "\tUser time (seconds): %U\n"
65 "\tSystem time (seconds): %S\n"
66 "\tPercent of CPU this job got: %P\n"
67 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
68 "\tAverage shared text size (kbytes): %X\n"
69 "\tAverage unshared data size (kbytes): %D\n"
70 "\tAverage stack size (kbytes): %p\n"
71 "\tAverage total size (kbytes): %K\n"
72 "\tMaximum resident set size (kbytes): %M\n"
73 "\tAverage resident set size (kbytes): %t\n"
74 "\tMajor (requiring I/O) page faults: %F\n"
75 "\tMinor (reclaiming a frame) page faults: %R\n"
76 "\tVoluntary context switches: %w\n"
77 "\tInvoluntary context switches: %c\n"
78 "\tSwaps: %W\n"
79 "\tFile system inputs: %I\n"
80 "\tFile system outputs: %O\n"
81 "\tSocket messages sent: %s\n"
82 "\tSocket messages received: %r\n"
83 "\tSignals delivered: %k\n"
Denis Vlasenkof93ab472006-12-22 12:36:13 +000084 "\tPage size (bytes): %Z\n"
85 "\tExit status: %x";
Eric Andersenc3657422001-11-30 07:54:32 +000086
Denis Vlasenkof93ab472006-12-22 12:36:13 +000087/* Wait for and fill in data on child process PID.
88 Return 0 on error, 1 if ok. */
Eric Andersenc3657422001-11-30 07:54:32 +000089/* pid_t is short on BSDI, so don't try to promote it. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +000090static void resuse_end(pid_t pid, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +000091{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000092 pid_t caught;
Eric Andersenc3657422001-11-30 07:54:32 +000093
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +000094 /* Ignore signals, but don't ignore the children. When wait3
Denys Vlasenko6830ade2013-01-15 13:58:01 +010095 * returns the child process, set the time the command finished. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +000096 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
97 if (caught == -1 && errno != EINTR) {
James Byrne69374872019-07-02 11:35:03 +020098 bb_simple_perror_msg("wait");
Denis Vlasenkob44c7902008-03-17 09:29:43 +000099 return;
100 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000101 }
Denys Vlasenkof2c8aa62010-01-12 12:52:30 +0100102 resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
Eric Andersenc3657422001-11-30 07:54:32 +0000103}
104
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000105static void printargv(char *const *argv)
Eric Andersenc3657422001-11-30 07:54:32 +0000106{
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000107 const char *fmt = " %s" + 1;
108 do {
109 printf(fmt, *argv);
110 fmt = " %s";
111 } while (*++argv);
Eric Andersenc3657422001-11-30 07:54:32 +0000112}
113
114/* Return the number of kilobytes corresponding to a number of pages PAGES.
115 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
116
117 Try to do arithmetic so that the risk of overflow errors is minimized.
118 This is funky since the pagesize could be less than 1K.
119 Note: Some machines express getrusage statistics in terms of K,
120 others in terms of pages. */
Denys Vlasenkoc7b858f2020-12-14 18:49:23 +0100121#ifdef BB_ARCH_FIXED_PAGESIZE
122# define pagesize BB_ARCH_FIXED_PAGESIZE
123# define ptok(pagesize, pages) ptok(pages)
124#endif
Bernhard Reutner-Fischer0adf7f22009-02-23 16:51:25 +0000125static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
Eric Andersenc3657422001-11-30 07:54:32 +0000126{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000127 unsigned long tmp;
Eric Andersenc3657422001-11-30 07:54:32 +0000128
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000129 /* Conversion. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000130 if (pages > (LONG_MAX / pagesize)) { /* Could overflow. */
131 tmp = pages / 1024; /* Smaller first, */
132 return tmp * pagesize; /* then larger. */
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000133 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000134 /* Could underflow. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000135 tmp = pages * pagesize; /* Larger first, */
136 return tmp / 1024; /* then smaller. */
Eric Andersenc3657422001-11-30 07:54:32 +0000137}
Denys Vlasenkoc7b858f2020-12-14 18:49:23 +0100138#undef pagesize
Eric Andersenc3657422001-11-30 07:54:32 +0000139
140/* summarize: Report on the system use of a command.
141
Denys Vlasenko95f79532017-08-02 14:26:33 +0200142 Print the FMT argument except that '%' sequences
143 have special meaning, and '\n' and '\t' are translated into
144 newline and tab, respectively, and '\\' is translated into '\'.
Eric Andersenc3657422001-11-30 07:54:32 +0000145
Denys Vlasenko95f79532017-08-02 14:26:33 +0200146 The character following a '%' can be:
Eric Andersenc3657422001-11-30 07:54:32 +0000147 (* means the tcsh time builtin also recognizes it)
Denys Vlasenko95f79532017-08-02 14:26:33 +0200148 % == a literal '%'
Eric Andersenc3657422001-11-30 07:54:32 +0000149 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 Vlasenko459be352007-06-17 19:09:05 +0000184#ifndef TICKS_PER_SEC
185#define TICKS_PER_SEC 100
186#endif
187
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000188static void summarize(const char *fmt, char **command, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000189{
Denis Vlasenko459be352007-06-17 19:09:05 +0000190 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
191 unsigned cpu_ticks; /* Same, in "CPU ticks" */
Denys Vlasenkoc7b858f2020-12-14 18:49:23 +0100192 unsigned pagesize = bb_getpagesize();
Eric Andersenc3657422001-11-30 07:54:32 +0000193
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000194 /* Impossible: we do not use WUNTRACED flag in wait()...
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000195 if (WIFSTOPPED(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000196 printf("Command stopped by signal %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000197 WSTOPSIG(resp->waitstatus));
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000198 else */
199 if (WIFSIGNALED(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000200 printf("Command terminated by signal %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000201 WTERMSIG(resp->waitstatus));
202 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
Denis Vlasenko459be352007-06-17 19:09:05 +0000203 printf("Command exited with non-zero status %u\n",
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000204 WEXITSTATUS(resp->waitstatus));
Eric Andersenc3657422001-11-30 07:54:32 +0000205
Denis Vlasenko459be352007-06-17 19:09:05 +0000206 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
207 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
Eric Andersenc3657422001-11-30 07:54:32 +0000208
Denis Vlasenkobd7bb292007-06-17 23:40:26 +0000209#if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000210 /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
Denis Vlasenkobd7bb292007-06-17 23:40:26 +0000211 cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
212#else
213 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
214#endif
Denis Vlasenko459be352007-06-17 19:09:05 +0000215 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
Eric Andersenc3657422001-11-30 07:54:32 +0000216
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000217 while (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000218 /* Handle leading literal part */
219 int n = strcspn(fmt, "%\\");
220 if (n) {
221 printf("%.*s", n, fmt);
222 fmt += n;
223 continue;
224 }
225
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000226 switch (*fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000227#ifdef NOT_NEEDED
228 /* Handle literal char */
229 /* Usually we optimize for size, but there is a limit
230 * for everything. With this we do a lot of 1-byte writes */
231 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000232 bb_putchar(*fmt);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000233 break;
234#endif
235
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000236 case '%':
237 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000238#ifdef NOT_NEEDED_YET
239 /* Our format strings do not have these */
240 /* and we do not take format str from user */
241 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000242 bb_putchar('%');
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000243 /*FALLTHROUGH*/
244 case '%':
245 if (!*fmt) goto ret;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000246 bb_putchar(*fmt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000247 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000248#endif
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000249 case 'C': /* The command that got timed. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000250 printargv(command);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000251 break;
252 case 'D': /* Average unshared data size. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000253 printf("%lu",
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000254 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
255 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000256 break;
Denis Vlasenko459be352007-06-17 19:09:05 +0000257 case 'E': { /* Elapsed real (wall clock) time. */
258 unsigned seconds = resp->elapsed_ms / 1000;
259 if (seconds >= 3600) /* One hour -> h:m:s. */
260 printf("%uh %um %02us",
261 seconds / 3600,
262 (seconds % 3600) / 60,
263 seconds % 60);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000264 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000265 printf("%um %u.%02us", /* -> m:s. */
266 seconds / 60,
267 seconds % 60,
268 (unsigned)(resp->elapsed_ms / 10) % 100);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000269 break;
Denis Vlasenko459be352007-06-17 19:09:05 +0000270 }
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000271 case 'F': /* Major page faults. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000272 printf("%lu", resp->ru.ru_majflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000273 break;
274 case 'I': /* Inputs. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000275 printf("%lu", resp->ru.ru_inblock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000276 break;
277 case 'K': /* Average mem usage == data+stack+text. */
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000278 printf("%lu",
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000279 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
280 ptok(pagesize, (UL) resp->ru.ru_isrss) +
281 ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000282 break;
283 case 'M': /* Maximum resident set size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000284 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000285 break;
286 case 'O': /* Outputs. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000287 printf("%lu", resp->ru.ru_oublock);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000288 break;
289 case 'P': /* Percent of CPU this job got. */
290 /* % cpu is (total cpu time)/(elapsed time). */
Denis Vlasenko459be352007-06-17 19:09:05 +0000291 if (resp->elapsed_ms > 0)
292 printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000293 else
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000294 printf("?%%");
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000295 break;
296 case 'R': /* Minor page faults (reclaims). */
Denis Vlasenko459be352007-06-17 19:09:05 +0000297 printf("%lu", resp->ru.ru_minflt);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000298 break;
299 case 'S': /* System time. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000300 printf("%u.%02u",
301 (unsigned)resp->ru.ru_stime.tv_sec,
302 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000303 break;
304 case 'T': /* System time. */
305 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000306 printf("%uh %um %02us",
307 (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
308 (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
309 (unsigned)(resp->ru.ru_stime.tv_sec % 60));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000310 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000311 printf("%um %u.%02us", /* -> m:s. */
312 (unsigned)(resp->ru.ru_stime.tv_sec / 60),
313 (unsigned)(resp->ru.ru_stime.tv_sec % 60),
314 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000315 break;
316 case 'U': /* User time. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000317 printf("%u.%02u",
318 (unsigned)resp->ru.ru_utime.tv_sec,
319 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000320 break;
321 case 'u': /* User time. */
322 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000323 printf("%uh %um %02us",
324 (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
325 (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
326 (unsigned)(resp->ru.ru_utime.tv_sec % 60));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000327 else
Denis Vlasenko459be352007-06-17 19:09:05 +0000328 printf("%um %u.%02us", /* -> m:s. */
329 (unsigned)(resp->ru.ru_utime.tv_sec / 60),
330 (unsigned)(resp->ru.ru_utime.tv_sec % 60),
331 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000332 break;
333 case 'W': /* Times swapped out. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000334 printf("%lu", resp->ru.ru_nswap);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000335 break;
336 case 'X': /* Average shared text size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000337 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000338 break;
339 case 'Z': /* Page size. */
Bernhard Reutner-Fischer0adf7f22009-02-23 16:51:25 +0000340 printf("%u", pagesize);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000341 break;
342 case 'c': /* Involuntary context switches. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000343 printf("%lu", resp->ru.ru_nivcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000344 break;
345 case 'e': /* Elapsed real time in seconds. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000346 printf("%u.%02u",
347 (unsigned)resp->elapsed_ms / 1000,
348 (unsigned)(resp->elapsed_ms / 10) % 100);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000349 break;
350 case 'k': /* Signals delivered. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000351 printf("%lu", resp->ru.ru_nsignals);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000352 break;
353 case 'p': /* Average stack segment. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000354 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000355 break;
356 case 'r': /* Incoming socket messages received. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000357 printf("%lu", resp->ru.ru_msgrcv);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000358 break;
359 case 's': /* Outgoing socket messages sent. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000360 printf("%lu", resp->ru.ru_msgsnd);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000361 break;
362 case 't': /* Average resident set size. */
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000363 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000364 break;
365 case 'w': /* Voluntary context switches. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000366 printf("%lu", resp->ru.ru_nvcsw);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000367 break;
368 case 'x': /* Exit status. */
Denis Vlasenko459be352007-06-17 19:09:05 +0000369 printf("%u", WEXITSTATUS(resp->waitstatus));
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000370 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000371 }
Eric Andersenc3657422001-11-30 07:54:32 +0000372 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000373
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000374#ifdef NOT_NEEDED_YET
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000375 case '\\': /* Format escape. */
376 switch (*++fmt) {
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000377 default:
Denis Vlasenko4daad902007-09-27 10:20:47 +0000378 bb_putchar('\\');
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000379 /*FALLTHROUGH*/
380 case '\\':
381 if (!*fmt) goto ret;
Denis Vlasenko4daad902007-09-27 10:20:47 +0000382 bb_putchar(*fmt);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000383 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000384 case 't':
Denis Vlasenko4daad902007-09-27 10:20:47 +0000385 bb_putchar('\t');
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000386 break;
387 case 'n':
Denis Vlasenko4daad902007-09-27 10:20:47 +0000388 bb_putchar('\n');
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000389 break;
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000390 }
Eric Andersenc3657422001-11-30 07:54:32 +0000391 break;
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000392#endif
Eric Andersenc3657422001-11-30 07:54:32 +0000393 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000394 ++fmt;
Eric Andersenc3657422001-11-30 07:54:32 +0000395 }
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000396 /* ret: */
Denis Vlasenko4daad902007-09-27 10:20:47 +0000397 bb_putchar('\n');
Eric Andersenc3657422001-11-30 07:54:32 +0000398}
399
400/* Run command CMD and return statistics on it.
401 Put the statistics in *RESP. */
Denis Vlasenko25591c32008-02-16 22:58:56 +0000402static void run_command(char *const *cmd, resource_t *resp)
Eric Andersenc3657422001-11-30 07:54:32 +0000403{
Pascal Bellard21e8e8d2010-07-04 00:57:03 +0200404 pid_t pid;
Denis Vlasenko25591c32008-02-16 22:58:56 +0000405 void (*interrupt_signal)(int);
406 void (*quit_signal)(int);
Eric Andersenc3657422001-11-30 07:54:32 +0000407
Denys Vlasenkof2c8aa62010-01-12 12:52:30 +0100408 resp->elapsed_ms = monotonic_ms();
Pascal Bellard926031b2010-07-04 15:32:38 +0200409 pid = xvfork();
Pascal Bellard21e8e8d2010-07-04 00:57:03 +0200410 if (pid == 0) {
411 /* Child */
412 BB_EXECVP_or_die((char**)cmd);
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000413 }
Eric Andersenc3657422001-11-30 07:54:32 +0000414
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000415 /* Have signals kill the child but not self (if possible). */
Denys Vlasenko10ad6222017-04-17 16:13:32 +0200416//TODO: just block all sigs? and re-enable them in the very end in main?
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000417 interrupt_signal = signal(SIGINT, SIG_IGN);
418 quit_signal = signal(SIGQUIT, SIG_IGN);
Eric Andersenc3657422001-11-30 07:54:32 +0000419
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000420 resuse_end(pid, resp);
Eric Andersenc3657422001-11-30 07:54:32 +0000421
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000422 /* Re-enable signals. */
423 signal(SIGINT, interrupt_signal);
424 signal(SIGQUIT, quit_signal);
Eric Andersenc3657422001-11-30 07:54:32 +0000425}
426
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000427int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000428int time_main(int argc UNUSED_PARAM, char **argv)
Eric Andersenc3657422001-11-30 07:54:32 +0000429{
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000430 resource_t res;
Denys Vlasenkoa1a3b592017-04-28 18:01:18 +0200431 /* $TIME has lowest prio (-v,-p,-f FMT overrride it) */
432 const char *output_format = getenv("TIME") ? : default_format;
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200433 char *output_filename;
434 int output_fd;
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000435 int opt;
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200436 int ex;
437 enum {
438 OPT_v = (1 << 0),
439 OPT_p = (1 << 1),
440 OPT_a = (1 << 2),
441 OPT_o = (1 << 3),
Denys Vlasenkoa1a3b592017-04-28 18:01:18 +0200442 OPT_f = (1 << 4),
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200443 };
Eric Andersenc3657422001-11-30 07:54:32 +0000444
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000445 /* "+": stop on first non-option */
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200446 opt = getopt32(argv, "^+" "vpao:f:" "\0" "-1"/*at least one arg*/,
447 &output_filename, &output_format
448 );
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000449 argv += optind;
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200450 if (opt & OPT_v)
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000451 output_format = long_format;
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200452 if (opt & OPT_p)
Denis Vlasenkob44c7902008-03-17 09:29:43 +0000453 output_format = posix_format;
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200454 output_fd = STDERR_FILENO;
455 if (opt & OPT_o) {
Denys Vlasenkod3a7e882017-10-27 19:05:00 +0200456#ifndef O_CLOEXEC
457# define O_CLOEXEC 0
458#endif
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200459 output_fd = xopen(output_filename,
460 (opt & OPT_a) /* append? */
461 ? (O_CREAT | O_WRONLY | O_CLOEXEC | O_APPEND)
462 : (O_CREAT | O_WRONLY | O_CLOEXEC | O_TRUNC)
463 );
Denys Vlasenkod3a7e882017-10-27 19:05:00 +0200464 if (!O_CLOEXEC)
465 close_on_exec_on(output_fd);
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200466 }
Eric Andersenc3657422001-11-30 07:54:32 +0000467
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000468 run_command(argv, &res);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000469
470 /* Cheat. printf's are shorter :) */
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200471 xdup2(output_fd, STDOUT_FILENO);
Denis Vlasenkoc5cb38f2006-12-22 13:43:19 +0000472 summarize(output_format, argv, &res);
Eric Andersenc3657422001-11-30 07:54:32 +0000473
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200474 ex = WEXITSTATUS(res.waitstatus);
475 /* Impossible: we do not use WUNTRACED flag in wait()...
Bernhard Reutner-Fischerb1312c92006-06-03 20:09:02 +0000476 if (WIFSTOPPED(res.waitstatus))
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200477 ex = WSTOPSIG(res.waitstatus);
478 */
Denis Vlasenkof93ab472006-12-22 12:36:13 +0000479 if (WIFSIGNALED(res.waitstatus))
Tommi Rantala5fe5be22017-04-28 17:54:14 +0200480 ex = WTERMSIG(res.waitstatus);
481
482 fflush_stdout_and_exit(ex);
Eric Andersenc3657422001-11-30 07:54:32 +0000483}