1 /* vi: set sw=4 ts=4: */
3 * 'time' utility to display resource usage of processes.
4 * Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
6 * Licensed under GPLv2, see file LICENSE in this source tree.
8 /* Originally written by David Keppel <pardo@cs.washington.edu>.
9 * Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
10 * Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
13 //config: bool "time (8.1 kb)"
16 //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.
20 //applet:IF_TIME(APPLET(time, BB_DIR_USR_BIN, BB_SUID_DROP))
22 //kbuild:lib-$(CONFIG_TIME) += time.o
24 //usage:#define time_trivial_usage
25 //usage: "[-vpa] [-o FILE] PROG ARGS"
26 //usage:#define time_full_usage "\n\n"
27 //usage: "Run PROG, display resource usage when it exits\n"
28 //usage: "\n -v Verbose"
29 //usage: "\n -p POSIX output format"
30 //usage: "\n -f FMT Custom format"
31 //usage: "\n -o FILE Write result to FILE"
32 //usage: "\n -a Append (else overwrite)"
37 static pid_t
wait3(int *status
, int options
, struct rusage
*rusage
)
39 return wait4(-1, status
, options
, rusage
);
43 /* Information on the resources used by a child process. */
47 unsigned elapsed_ms
; /* Wallclock time of process. */
50 /* msec = milliseconds = 1/1,000 (1*10e-3) second.
51 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
53 #define UL unsigned long
55 static const char default_format
[] ALIGN1
= "real\t%E\nuser\t%u\nsys\t%T";
57 /* The output format for the -p option .*/
58 static const char posix_format
[] ALIGN1
= "real %e\nuser %U\nsys %S";
60 /* Format string for printing all statistics verbosely.
61 Keep this output to 24 lines so users on terminals can see it all.*/
62 static const char long_format
[] ALIGN1
=
63 "\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"
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"
84 "\tPage size (bytes): %Z\n"
87 /* Wait for and fill in data on child process PID.
88 Return 0 on error, 1 if ok. */
89 /* pid_t is short on BSDI, so don't try to promote it. */
90 static void resuse_end(pid_t pid
, resource_t
*resp
)
94 /* Ignore signals, but don't ignore the children. When wait3
95 * returns the child process, set the time the command finished. */
96 while ((caught
= wait3(&resp
->waitstatus
, 0, &resp
->ru
)) != pid
) {
97 if (caught
== -1 && errno
!= EINTR
) {
98 bb_simple_perror_msg("wait");
102 resp
->elapsed_ms
= monotonic_ms() - resp
->elapsed_ms
;
105 static void printargv(char *const *argv
)
107 const char *fmt
= " %s" + 1;
115 /* Return the number of kilobytes corresponding to a number of pages PAGES.
116 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
118 Try to do arithmetic so that the risk of overflow errors is minimized.
119 This is funky since the pagesize could be less than 1K.
120 Note: Some machines express getrusage statistics in terms of K,
121 others in terms of pages. */
122 #ifdef BB_ARCH_FIXED_PAGESIZE
123 # define pagesize BB_ARCH_FIXED_PAGESIZE
124 # define ptok(pagesize, pages) ptok(pages)
126 static unsigned long ptok(const unsigned pagesize
, const unsigned long pages
)
131 if (pages
> (LONG_MAX
/ pagesize
)) { /* Could overflow. */
132 tmp
= pages
/ 1024; /* Smaller first, */
133 return tmp
* pagesize
; /* then larger. */
135 /* Could underflow. */
136 tmp
= pages
* pagesize
; /* Larger first, */
137 return tmp
/ 1024; /* then smaller. */
142 /* summarize: Report on the system use of a command.
144 Print the FMT argument except that '%' sequences
145 have special meaning, and '\n' and '\t' are translated into
146 newline and tab, respectively, and '\\' is translated into '\'.
148 The character following a '%' can be:
149 (* means the tcsh time builtin also recognizes it)
151 C == command name and arguments
152 * D == average unshared data size in K (ru_idrss+ru_isrss)
153 * E == elapsed real (wall clock) time in [hour:]min:sec
154 * F == major page faults (required physical I/O) (ru_majflt)
155 * I == file system inputs (ru_inblock)
156 * K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
157 * M == maximum resident set size in K (ru_maxrss)
158 * O == file system outputs (ru_oublock)
159 * P == percent of CPU this job got (total cpu time / elapsed time)
160 * R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
161 * S == system (kernel) time (seconds) (ru_stime)
162 * T == system time in [hour:]min:sec
163 * U == user time (seconds) (ru_utime)
164 * u == user time in [hour:]min:sec
165 * W == times swapped out (ru_nswap)
166 * X == average amount of shared text in K (ru_ixrss)
168 * c == involuntary context switches (ru_nivcsw)
169 e == elapsed real time in seconds
170 * k == signals delivered (ru_nsignals)
171 p == average unshared stack size in K (ru_isrss)
172 * r == socket messages received (ru_msgrcv)
173 * s == socket messages sent (ru_msgsnd)
174 t == average resident set size in K (ru_idrss)
175 * w == voluntary context switches (ru_nvcsw)
176 x == exit status of command
178 Various memory usages are found by converting from page-seconds
179 to kbytes by multiplying by the page size, dividing by 1024,
180 and dividing by elapsed real time.
182 FMT is the format string, interpreted as described above.
183 COMMAND is the command and args that are being summarized.
184 RESP is resource information on the command. */
186 #ifndef TICKS_PER_SEC
187 #define TICKS_PER_SEC 100
190 static void summarize(const char *fmt
, char **command
, resource_t
*resp
)
192 unsigned vv_ms
; /* Elapsed virtual (CPU) milliseconds */
193 unsigned cpu_ticks
; /* Same, in "CPU ticks" */
194 unsigned pagesize
= bb_getpagesize();
196 /* Impossible: we do not use WUNTRACED flag in wait()...
197 if (WIFSTOPPED(resp->waitstatus))
198 printf("Command stopped by signal %u\n",
199 WSTOPSIG(resp->waitstatus));
201 if (WIFSIGNALED(resp
->waitstatus
))
202 printf("Command terminated by signal %u\n",
203 WTERMSIG(resp
->waitstatus
));
204 else if (WIFEXITED(resp
->waitstatus
) && WEXITSTATUS(resp
->waitstatus
))
205 printf("Command exited with non-zero status %u\n",
206 WEXITSTATUS(resp
->waitstatus
));
208 vv_ms
= (resp
->ru
.ru_utime
.tv_sec
+ resp
->ru
.ru_stime
.tv_sec
) * 1000
209 + (resp
->ru
.ru_utime
.tv_usec
+ resp
->ru
.ru_stime
.tv_usec
) / 1000;
211 #if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
212 /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
213 cpu_ticks
= vv_ms
/ (1000 / TICKS_PER_SEC
);
215 cpu_ticks
= vv_ms
* (unsigned long long)TICKS_PER_SEC
/ 1000;
217 if (!cpu_ticks
) cpu_ticks
= 1; /* we divide by it, must be nonzero */
220 /* Handle leading literal part */
221 int n
= strcspn(fmt
, "%\\");
223 printf("%.*s", n
, fmt
);
232 /* Unknown %<char> is printed as "?<char>" */
235 /* Trailing -f '...%' prints "...?" but NOT newline */
242 case 'C': /* The command that got timed. */
245 case 'D': /* Average unshared data size. */
246 /* (linux kernel sets ru_idrss/isrss/ixrss to 0,
247 * docs say the value is in kbytes, so ptok() is wrong) */
249 (/*ptok(pagesize,*/ (UL
) resp
->ru
.ru_idrss
+
250 (UL
) resp
->ru
.ru_isrss
254 case 'E': { /* Elapsed real (wall clock) time. */
255 unsigned seconds
= resp
->elapsed_ms
/ 1000;
256 if (seconds
>= 3600) /* One hour -> h:m:s. */
257 printf("%uh %um %02us",
259 (seconds
% 3600) / 60,
262 printf("%um %u.%02us", /* -> m:s. */
265 (unsigned)(resp
->elapsed_ms
/ 10) % 100);
268 case 'F': /* Major page faults. */
269 printf("%lu", resp
->ru
.ru_majflt
);
271 case 'I': /* Inputs. */
272 printf("%lu", resp
->ru
.ru_inblock
);
274 case 'K': /* Average mem usage == data+stack+text. */
275 /* (linux kernel sets ru_idrss/isrss/ixrss to 0,
276 * docs say the value is in kbytes, so ptok() is wrong) */
278 (/*ptok(pagesize,*/ (UL
) resp
->ru
.ru_idrss
+
279 (UL
) resp
->ru
.ru_isrss
+
280 (UL
) resp
->ru
.ru_ixrss
284 case 'M': /* Maximum resident set size. */
285 printf("%lu", (UL
) resp
->ru
.ru_maxrss
);
287 case 'O': /* Outputs. */
288 printf("%lu", resp
->ru
.ru_oublock
);
290 case 'P': /* Percent of CPU this job got. */
291 /* % cpu is (total cpu time)/(elapsed time). */
292 if (resp
->elapsed_ms
> 0)
293 printf("%u%%", (unsigned)(vv_ms
* 100 / resp
->elapsed_ms
));
297 case 'R': /* Minor page faults (reclaims). */
298 printf("%lu", resp
->ru
.ru_minflt
);
300 case 'S': /* System time. */
302 (unsigned)resp
->ru
.ru_stime
.tv_sec
,
303 (unsigned)(resp
->ru
.ru_stime
.tv_usec
/ 10000));
305 case 'T': /* System time. */
306 if (resp
->ru
.ru_stime
.tv_sec
>= 3600) /* One hour -> h:m:s. */
307 printf("%uh %um %02us",
308 (unsigned)(resp
->ru
.ru_stime
.tv_sec
/ 3600),
309 (unsigned)(resp
->ru
.ru_stime
.tv_sec
% 3600) / 60,
310 (unsigned)(resp
->ru
.ru_stime
.tv_sec
% 60));
312 printf("%um %u.%02us", /* -> m:s. */
313 (unsigned)(resp
->ru
.ru_stime
.tv_sec
/ 60),
314 (unsigned)(resp
->ru
.ru_stime
.tv_sec
% 60),
315 (unsigned)(resp
->ru
.ru_stime
.tv_usec
/ 10000));
317 case 'U': /* User time. */
319 (unsigned)resp
->ru
.ru_utime
.tv_sec
,
320 (unsigned)(resp
->ru
.ru_utime
.tv_usec
/ 10000));
322 case 'u': /* User time. */
323 if (resp
->ru
.ru_utime
.tv_sec
>= 3600) /* One hour -> h:m:s. */
324 printf("%uh %um %02us",
325 (unsigned)(resp
->ru
.ru_utime
.tv_sec
/ 3600),
326 (unsigned)(resp
->ru
.ru_utime
.tv_sec
% 3600) / 60,
327 (unsigned)(resp
->ru
.ru_utime
.tv_sec
% 60));
329 printf("%um %u.%02us", /* -> m:s. */
330 (unsigned)(resp
->ru
.ru_utime
.tv_sec
/ 60),
331 (unsigned)(resp
->ru
.ru_utime
.tv_sec
% 60),
332 (unsigned)(resp
->ru
.ru_utime
.tv_usec
/ 10000));
334 case 'W': /* Times swapped out. */
335 printf("%lu", resp
->ru
.ru_nswap
);
337 case 'X': /* Average shared text size. */
338 printf("%lu", /*ptok(pagesize,*/ (UL
) resp
->ru
.ru_ixrss
/ cpu_ticks
);
340 case 'Z': /* Page size. */
341 printf("%u", pagesize
);
343 case 'c': /* Involuntary context switches. */
344 printf("%lu", resp
->ru
.ru_nivcsw
);
346 case 'e': /* Elapsed real time in seconds. */
348 (unsigned)resp
->elapsed_ms
/ 1000,
349 (unsigned)(resp
->elapsed_ms
/ 10) % 100);
351 case 'k': /* Signals delivered. */
352 printf("%lu", resp
->ru
.ru_nsignals
);
354 case 'p': /* Average stack segment. */
355 printf("%lu", /*ptok(pagesize,*/ (UL
) resp
->ru
.ru_isrss
/ cpu_ticks
);
357 case 'r': /* Incoming socket messages received. */
358 printf("%lu", resp
->ru
.ru_msgrcv
);
360 case 's': /* Outgoing socket messages sent. */
361 printf("%lu", resp
->ru
.ru_msgsnd
);
363 case 't': /* Average resident set size. */
364 printf("%lu", /*ptok(pagesize,*/ (UL
) resp
->ru
.ru_idrss
/ cpu_ticks
);
366 case 'w': /* Voluntary context switches. */
367 printf("%lu", resp
->ru
.ru_nvcsw
);
369 case 'x': /* Exit status. */
370 printf("%u", WEXITSTATUS(resp
->waitstatus
));
375 default: /* *fmt is '\': format escape */
378 /* Unknown \<char> is printed as "?\<char>" */
382 /* Trailing -f '...\': GNU time 1.9 prints
383 * "...?\COMMAND" (it's probably a bug).
407 /* Run command CMD and return statistics on it.
408 Put the statistics in *RESP. */
409 static void run_command(char *const *cmd
, resource_t
*resp
)
412 void (*interrupt_signal
)(int);
413 void (*quit_signal
)(int);
415 resp
->elapsed_ms
= monotonic_ms();
419 BB_EXECVP_or_die((char**)cmd
);
422 /* Have signals kill the child but not self (if possible). */
423 //TODO: just block all sigs? and re-enable them in the very end in main?
424 interrupt_signal
= signal(SIGINT
, SIG_IGN
);
425 quit_signal
= signal(SIGQUIT
, SIG_IGN
);
427 resuse_end(pid
, resp
);
429 /* Re-enable signals. */
430 signal(SIGINT
, interrupt_signal
);
431 signal(SIGQUIT
, quit_signal
);
434 int time_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
435 int time_main(int argc UNUSED_PARAM
, char **argv
)
438 /* $TIME has lowest prio (-v,-p,-f FMT override it) */
439 const char *output_format
= getenv("TIME") ? : default_format
;
440 char *output_filename
;
452 /* "+": stop on first non-option */
453 opt
= getopt32(argv
, "^+" "vpao:f:" "\0" "-1"/*at least one arg*/,
454 &output_filename
, &output_format
458 output_format
= long_format
;
460 output_format
= posix_format
;
461 output_fd
= STDERR_FILENO
;
466 output_fd
= xopen(output_filename
,
467 (opt
& OPT_a
) /* append? */
468 ? (O_CREAT
| O_WRONLY
| O_CLOEXEC
| O_APPEND
)
469 : (O_CREAT
| O_WRONLY
| O_CLOEXEC
| O_TRUNC
)
472 close_on_exec_on(output_fd
);
475 run_command(argv
, &res
);
477 /* Cheat. printf's are shorter :) */
478 xdup2(output_fd
, STDOUT_FILENO
);
479 summarize(output_format
, argv
, &res
);
481 ex
= WEXITSTATUS(res
.waitstatus
);
482 /* Impossible: we do not use WUNTRACED flag in wait()...
483 if (WIFSTOPPED(res.waitstatus))
484 ex = WSTOPSIG(res.waitstatus);
486 if (WIFSIGNALED(res
.waitstatus
))
487 ex
= WTERMSIG(res
.waitstatus
);
489 fflush_stdout_and_exit(ex
);