1 /* vi: set sw=4 ts=4: */
2 /* 'time' utility to display resource usage of processes.
3 Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
5 Licensed under GPLv2, see file LICENSE in this source tree.
7 /* Originally written by David Keppel <pardo@cs.washington.edu>.
8 Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
9 Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
15 //config: The time command runs the specified program with the given arguments.
16 //config: When the command finishes, time writes a message to standard output
17 //config: giving timing statistics about this program run.
19 //applet:IF_TIME(APPLET(time, BB_DIR_USR_BIN, BB_SUID_DROP))
21 //kbuild:lib-$(CONFIG_TIME) += time.o
23 //usage:#define time_trivial_usage
24 //usage: "[-vpa] [-o FILE] PROG ARGS"
25 //usage:#define time_full_usage "\n\n"
26 //usage: "Run PROG, display resource usage when it exits\n"
27 //usage: "\n -v Verbose"
28 //usage: "\n -p POSIX output format"
29 //usage: "\n -o FILE Write result to FILE"
30 //usage: "\n -a Append (else overwrite)"
33 #include <sys/resource.h> /* getrusage */
35 /* Information on the resources used by a child process. */
39 unsigned elapsed_ms; /* Wallclock time of process. */
42 /* msec = milliseconds = 1/1,000 (1*10e-3) second.
43 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
45 #define UL unsigned long
47 static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
49 /* The output format for the -p option .*/
50 static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
52 /* Format string for printing all statistics verbosely.
53 Keep this output to 24 lines so users on terminals can see it all.*/
54 static const char long_format[] ALIGN1 =
55 "\tCommand being timed: \"%C\"\n"
56 "\tUser time (seconds): %U\n"
57 "\tSystem time (seconds): %S\n"
58 "\tPercent of CPU this job got: %P\n"
59 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
60 "\tAverage shared text size (kbytes): %X\n"
61 "\tAverage unshared data size (kbytes): %D\n"
62 "\tAverage stack size (kbytes): %p\n"
63 "\tAverage total size (kbytes): %K\n"
64 "\tMaximum resident set size (kbytes): %M\n"
65 "\tAverage resident set size (kbytes): %t\n"
66 "\tMajor (requiring I/O) page faults: %F\n"
67 "\tMinor (reclaiming a frame) page faults: %R\n"
68 "\tVoluntary context switches: %w\n"
69 "\tInvoluntary context switches: %c\n"
71 "\tFile system inputs: %I\n"
72 "\tFile system outputs: %O\n"
73 "\tSocket messages sent: %s\n"
74 "\tSocket messages received: %r\n"
75 "\tSignals delivered: %k\n"
76 "\tPage size (bytes): %Z\n"
79 /* Wait for and fill in data on child process PID.
80 Return 0 on error, 1 if ok. */
81 /* pid_t is short on BSDI, so don't try to promote it. */
82 static void resuse_end(pid_t pid, resource_t *resp)
86 /* Ignore signals, but don't ignore the children. When wait3
87 * returns the child process, set the time the command finished. */
88 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
89 if (caught == -1 && errno != EINTR) {
90 bb_perror_msg("wait");
94 resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
97 static void printargv(char *const *argv)
99 const char *fmt = " %s" + 1;
106 /* Return the number of kilobytes corresponding to a number of pages PAGES.
107 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
109 Try to do arithmetic so that the risk of overflow errors is minimized.
110 This is funky since the pagesize could be less than 1K.
111 Note: Some machines express getrusage statistics in terms of K,
112 others in terms of pages. */
113 static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
118 if (pages > (LONG_MAX / pagesize)) { /* Could overflow. */
119 tmp = pages / 1024; /* Smaller first, */
120 return tmp * pagesize; /* then larger. */
122 /* Could underflow. */
123 tmp = pages * pagesize; /* Larger first, */
124 return tmp / 1024; /* then smaller. */
127 /* summarize: Report on the system use of a command.
129 Print the FMT argument except that `%' sequences
130 have special meaning, and `\n' and `\t' are translated into
131 newline and tab, respectively, and `\\' is translated into `\'.
133 The character following a `%' can be:
134 (* means the tcsh time builtin also recognizes it)
136 C == command name and arguments
137 * D == average unshared data size in K (ru_idrss+ru_isrss)
138 * E == elapsed real (wall clock) time in [hour:]min:sec
139 * F == major page faults (required physical I/O) (ru_majflt)
140 * I == file system inputs (ru_inblock)
141 * K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
142 * M == maximum resident set size in K (ru_maxrss)
143 * O == file system outputs (ru_oublock)
144 * P == percent of CPU this job got (total cpu time / elapsed time)
145 * R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
146 * S == system (kernel) time (seconds) (ru_stime)
147 * T == system time in [hour:]min:sec
148 * U == user time (seconds) (ru_utime)
149 * u == user time in [hour:]min:sec
150 * W == times swapped out (ru_nswap)
151 * X == average amount of shared text in K (ru_ixrss)
153 * c == involuntary context switches (ru_nivcsw)
154 e == elapsed real time in seconds
155 * k == signals delivered (ru_nsignals)
156 p == average unshared stack size in K (ru_isrss)
157 * r == socket messages received (ru_msgrcv)
158 * s == socket messages sent (ru_msgsnd)
159 t == average resident set size in K (ru_idrss)
160 * w == voluntary context switches (ru_nvcsw)
161 x == exit status of command
163 Various memory usages are found by converting from page-seconds
164 to kbytes by multiplying by the page size, dividing by 1024,
165 and dividing by elapsed real time.
167 FMT is the format string, interpreted as described above.
168 COMMAND is the command and args that are being summarized.
169 RESP is resource information on the command. */
171 #ifndef TICKS_PER_SEC
172 #define TICKS_PER_SEC 100
175 static void summarize(const char *fmt, char **command, resource_t *resp)
177 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
178 unsigned cpu_ticks; /* Same, in "CPU ticks" */
179 unsigned pagesize = getpagesize();
181 /* Impossible: we do not use WUNTRACED flag in wait()...
182 if (WIFSTOPPED(resp->waitstatus))
183 printf("Command stopped by signal %u\n",
184 WSTOPSIG(resp->waitstatus));
186 if (WIFSIGNALED(resp->waitstatus))
187 printf("Command terminated by signal %u\n",
188 WTERMSIG(resp->waitstatus));
189 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
190 printf("Command exited with non-zero status %u\n",
191 WEXITSTATUS(resp->waitstatus));
193 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
194 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
196 #if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
197 /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
198 cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
200 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
202 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
205 /* Handle leading literal part */
206 int n = strcspn(fmt, "%\\");
208 printf("%.*s", n, fmt);
215 /* Handle literal char */
216 /* Usually we optimize for size, but there is a limit
217 * for everything. With this we do a lot of 1-byte writes */
225 #ifdef NOT_NEEDED_YET
226 /* Our format strings do not have these */
227 /* and we do not take format str from user */
236 case 'C': /* The command that got timed. */
239 case 'D': /* Average unshared data size. */
241 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
242 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
244 case 'E': { /* Elapsed real (wall clock) time. */
245 unsigned seconds = resp->elapsed_ms / 1000;
246 if (seconds >= 3600) /* One hour -> h:m:s. */
247 printf("%uh %um %02us",
249 (seconds % 3600) / 60,
252 printf("%um %u.%02us", /* -> m:s. */
255 (unsigned)(resp->elapsed_ms / 10) % 100);
258 case 'F': /* Major page faults. */
259 printf("%lu", resp->ru.ru_majflt);
261 case 'I': /* Inputs. */
262 printf("%lu", resp->ru.ru_inblock);
264 case 'K': /* Average mem usage == data+stack+text. */
266 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
267 ptok(pagesize, (UL) resp->ru.ru_isrss) +
268 ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
270 case 'M': /* Maximum resident set size. */
271 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
273 case 'O': /* Outputs. */
274 printf("%lu", resp->ru.ru_oublock);
276 case 'P': /* Percent of CPU this job got. */
277 /* % cpu is (total cpu time)/(elapsed time). */
278 if (resp->elapsed_ms > 0)
279 printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
283 case 'R': /* Minor page faults (reclaims). */
284 printf("%lu", resp->ru.ru_minflt);
286 case 'S': /* System time. */
288 (unsigned)resp->ru.ru_stime.tv_sec,
289 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
291 case 'T': /* System time. */
292 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
293 printf("%uh %um %02us",
294 (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
295 (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
296 (unsigned)(resp->ru.ru_stime.tv_sec % 60));
298 printf("%um %u.%02us", /* -> m:s. */
299 (unsigned)(resp->ru.ru_stime.tv_sec / 60),
300 (unsigned)(resp->ru.ru_stime.tv_sec % 60),
301 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
303 case 'U': /* User time. */
305 (unsigned)resp->ru.ru_utime.tv_sec,
306 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
308 case 'u': /* User time. */
309 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
310 printf("%uh %um %02us",
311 (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
312 (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
313 (unsigned)(resp->ru.ru_utime.tv_sec % 60));
315 printf("%um %u.%02us", /* -> m:s. */
316 (unsigned)(resp->ru.ru_utime.tv_sec / 60),
317 (unsigned)(resp->ru.ru_utime.tv_sec % 60),
318 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
320 case 'W': /* Times swapped out. */
321 printf("%lu", resp->ru.ru_nswap);
323 case 'X': /* Average shared text size. */
324 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
326 case 'Z': /* Page size. */
327 printf("%u", pagesize);
329 case 'c': /* Involuntary context switches. */
330 printf("%lu", resp->ru.ru_nivcsw);
332 case 'e': /* Elapsed real time in seconds. */
334 (unsigned)resp->elapsed_ms / 1000,
335 (unsigned)(resp->elapsed_ms / 10) % 100);
337 case 'k': /* Signals delivered. */
338 printf("%lu", resp->ru.ru_nsignals);
340 case 'p': /* Average stack segment. */
341 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
343 case 'r': /* Incoming socket messages received. */
344 printf("%lu", resp->ru.ru_msgrcv);
346 case 's': /* Outgoing socket messages sent. */
347 printf("%lu", resp->ru.ru_msgsnd);
349 case 't': /* Average resident set size. */
350 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
352 case 'w': /* Voluntary context switches. */
353 printf("%lu", resp->ru.ru_nvcsw);
355 case 'x': /* Exit status. */
356 printf("%u", WEXITSTATUS(resp->waitstatus));
361 #ifdef NOT_NEEDED_YET
362 case '\\': /* Format escape. */
387 /* Run command CMD and return statistics on it.
388 Put the statistics in *RESP. */
389 static void run_command(char *const *cmd, resource_t *resp)
392 void (*interrupt_signal)(int);
393 void (*quit_signal)(int);
395 resp->elapsed_ms = monotonic_ms();
399 BB_EXECVP_or_die((char**)cmd);
402 /* Have signals kill the child but not self (if possible). */
403 //TODO: just block all sigs? and re-enable them in the very end in main?
404 interrupt_signal = signal(SIGINT, SIG_IGN);
405 quit_signal = signal(SIGQUIT, SIG_IGN);
407 resuse_end(pid, resp);
409 /* Re-enable signals. */
410 signal(SIGINT, interrupt_signal);
411 signal(SIGQUIT, quit_signal);
414 int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
415 int time_main(int argc UNUSED_PARAM, char **argv)
418 const char *output_format = default_format;
419 char *output_filename;
430 opt_complementary = "-1"; /* at least one arg */
431 /* "+": stop on first non-option */
432 opt = getopt32(argv, "+vpao:", &output_filename);
435 output_format = long_format;
437 output_format = posix_format;
438 output_fd = STDERR_FILENO;
440 output_fd = xopen(output_filename,
441 (opt & OPT_a) /* append? */
442 ? (O_CREAT | O_WRONLY | O_CLOEXEC | O_APPEND)
443 : (O_CREAT | O_WRONLY | O_CLOEXEC | O_TRUNC)
447 run_command(argv, &res);
449 /* Cheat. printf's are shorter :) */
450 xdup2(output_fd, STDOUT_FILENO);
451 summarize(output_format, argv, &res);
453 ex = WEXITSTATUS(res.waitstatus);
454 /* Impossible: we do not use WUNTRACED flag in wait()...
455 if (WIFSTOPPED(res.waitstatus))
456 ex = WSTOPSIG(res.waitstatus);
458 if (WIFSIGNALED(res.waitstatus))
459 ex = WTERMSIG(res.waitstatus);
461 fflush_stdout_and_exit(ex);