Changes for kernel and Busybox
[tomato.git] / release / src / router / busybox / miscutils / time.c
blob945f15f0d56c7eee322dbfdfdc68dcc4c6b44c83
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.
6 */
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>
12 //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"
16 //usage: "\n -v Verbose"
18 #include "libbb.h"
20 /* Information on the resources used by a child process. */
21 typedef struct {
22 int waitstatus;
23 struct rusage ru;
24 unsigned elapsed_ms; /* Wallclock time of process. */
25 } resource_t;
27 /* msec = milliseconds = 1/1,000 (1*10e-3) second.
28 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
30 #define UL unsigned long
32 static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
34 /* The output format for the -p option .*/
35 static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
37 /* Format string for printing all statistics verbosely.
38 Keep this output to 24 lines so users on terminals can see it all.*/
39 static const char long_format[] ALIGN1 =
40 "\tCommand being timed: \"%C\"\n"
41 "\tUser time (seconds): %U\n"
42 "\tSystem time (seconds): %S\n"
43 "\tPercent of CPU this job got: %P\n"
44 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
45 "\tAverage shared text size (kbytes): %X\n"
46 "\tAverage unshared data size (kbytes): %D\n"
47 "\tAverage stack size (kbytes): %p\n"
48 "\tAverage total size (kbytes): %K\n"
49 "\tMaximum resident set size (kbytes): %M\n"
50 "\tAverage resident set size (kbytes): %t\n"
51 "\tMajor (requiring I/O) page faults: %F\n"
52 "\tMinor (reclaiming a frame) page faults: %R\n"
53 "\tVoluntary context switches: %w\n"
54 "\tInvoluntary context switches: %c\n"
55 "\tSwaps: %W\n"
56 "\tFile system inputs: %I\n"
57 "\tFile system outputs: %O\n"
58 "\tSocket messages sent: %s\n"
59 "\tSocket messages received: %r\n"
60 "\tSignals delivered: %k\n"
61 "\tPage size (bytes): %Z\n"
62 "\tExit status: %x";
64 /* Wait for and fill in data on child process PID.
65 Return 0 on error, 1 if ok. */
66 /* pid_t is short on BSDI, so don't try to promote it. */
67 static void resuse_end(pid_t pid, resource_t *resp)
69 pid_t caught;
71 /* Ignore signals, but don't ignore the children. When wait3
72 returns the child process, set the time the command finished. */
73 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
74 if (caught == -1 && errno != EINTR) {
75 bb_perror_msg("wait");
76 return;
79 resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
82 static void printargv(char *const *argv)
84 const char *fmt = " %s" + 1;
85 do {
86 printf(fmt, *argv);
87 fmt = " %s";
88 } while (*++argv);
91 /* Return the number of kilobytes corresponding to a number of pages PAGES.
92 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
94 Try to do arithmetic so that the risk of overflow errors is minimized.
95 This is funky since the pagesize could be less than 1K.
96 Note: Some machines express getrusage statistics in terms of K,
97 others in terms of pages. */
98 static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
100 unsigned long tmp;
102 /* Conversion. */
103 if (pages > (LONG_MAX / pagesize)) { /* Could overflow. */
104 tmp = pages / 1024; /* Smaller first, */
105 return tmp * pagesize; /* then larger. */
107 /* Could underflow. */
108 tmp = pages * pagesize; /* Larger first, */
109 return tmp / 1024; /* then smaller. */
112 /* summarize: Report on the system use of a command.
114 Print the FMT argument except that `%' sequences
115 have special meaning, and `\n' and `\t' are translated into
116 newline and tab, respectively, and `\\' is translated into `\'.
118 The character following a `%' can be:
119 (* means the tcsh time builtin also recognizes it)
120 % == a literal `%'
121 C == command name and arguments
122 * D == average unshared data size in K (ru_idrss+ru_isrss)
123 * E == elapsed real (wall clock) time in [hour:]min:sec
124 * F == major page faults (required physical I/O) (ru_majflt)
125 * I == file system inputs (ru_inblock)
126 * K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
127 * M == maximum resident set size in K (ru_maxrss)
128 * O == file system outputs (ru_oublock)
129 * P == percent of CPU this job got (total cpu time / elapsed time)
130 * R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
131 * S == system (kernel) time (seconds) (ru_stime)
132 * T == system time in [hour:]min:sec
133 * U == user time (seconds) (ru_utime)
134 * u == user time in [hour:]min:sec
135 * W == times swapped out (ru_nswap)
136 * X == average amount of shared text in K (ru_ixrss)
137 Z == page size
138 * c == involuntary context switches (ru_nivcsw)
139 e == elapsed real time in seconds
140 * k == signals delivered (ru_nsignals)
141 p == average unshared stack size in K (ru_isrss)
142 * r == socket messages received (ru_msgrcv)
143 * s == socket messages sent (ru_msgsnd)
144 t == average resident set size in K (ru_idrss)
145 * w == voluntary context switches (ru_nvcsw)
146 x == exit status of command
148 Various memory usages are found by converting from page-seconds
149 to kbytes by multiplying by the page size, dividing by 1024,
150 and dividing by elapsed real time.
152 FMT is the format string, interpreted as described above.
153 COMMAND is the command and args that are being summarized.
154 RESP is resource information on the command. */
156 #ifndef TICKS_PER_SEC
157 #define TICKS_PER_SEC 100
158 #endif
160 static void summarize(const char *fmt, char **command, resource_t *resp)
162 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
163 unsigned cpu_ticks; /* Same, in "CPU ticks" */
164 unsigned pagesize = getpagesize();
166 /* Impossible: we do not use WUNTRACED flag in wait()...
167 if (WIFSTOPPED(resp->waitstatus))
168 printf("Command stopped by signal %u\n",
169 WSTOPSIG(resp->waitstatus));
170 else */
171 if (WIFSIGNALED(resp->waitstatus))
172 printf("Command terminated by signal %u\n",
173 WTERMSIG(resp->waitstatus));
174 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
175 printf("Command exited with non-zero status %u\n",
176 WEXITSTATUS(resp->waitstatus));
178 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
179 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
181 #if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
182 /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
183 cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
184 #else
185 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
186 #endif
187 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
189 while (*fmt) {
190 /* Handle leading literal part */
191 int n = strcspn(fmt, "%\\");
192 if (n) {
193 printf("%.*s", n, fmt);
194 fmt += n;
195 continue;
198 switch (*fmt) {
199 #ifdef NOT_NEEDED
200 /* Handle literal char */
201 /* Usually we optimize for size, but there is a limit
202 * for everything. With this we do a lot of 1-byte writes */
203 default:
204 bb_putchar(*fmt);
205 break;
206 #endif
208 case '%':
209 switch (*++fmt) {
210 #ifdef NOT_NEEDED_YET
211 /* Our format strings do not have these */
212 /* and we do not take format str from user */
213 default:
214 bb_putchar('%');
215 /*FALLTHROUGH*/
216 case '%':
217 if (!*fmt) goto ret;
218 bb_putchar(*fmt);
219 break;
220 #endif
221 case 'C': /* The command that got timed. */
222 printargv(command);
223 break;
224 case 'D': /* Average unshared data size. */
225 printf("%lu",
226 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
227 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
228 break;
229 case 'E': { /* Elapsed real (wall clock) time. */
230 unsigned seconds = resp->elapsed_ms / 1000;
231 if (seconds >= 3600) /* One hour -> h:m:s. */
232 printf("%uh %um %02us",
233 seconds / 3600,
234 (seconds % 3600) / 60,
235 seconds % 60);
236 else
237 printf("%um %u.%02us", /* -> m:s. */
238 seconds / 60,
239 seconds % 60,
240 (unsigned)(resp->elapsed_ms / 10) % 100);
241 break;
243 case 'F': /* Major page faults. */
244 printf("%lu", resp->ru.ru_majflt);
245 break;
246 case 'I': /* Inputs. */
247 printf("%lu", resp->ru.ru_inblock);
248 break;
249 case 'K': /* Average mem usage == data+stack+text. */
250 printf("%lu",
251 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
252 ptok(pagesize, (UL) resp->ru.ru_isrss) +
253 ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
254 break;
255 case 'M': /* Maximum resident set size. */
256 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
257 break;
258 case 'O': /* Outputs. */
259 printf("%lu", resp->ru.ru_oublock);
260 break;
261 case 'P': /* Percent of CPU this job got. */
262 /* % cpu is (total cpu time)/(elapsed time). */
263 if (resp->elapsed_ms > 0)
264 printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
265 else
266 printf("?%%");
267 break;
268 case 'R': /* Minor page faults (reclaims). */
269 printf("%lu", resp->ru.ru_minflt);
270 break;
271 case 'S': /* System time. */
272 printf("%u.%02u",
273 (unsigned)resp->ru.ru_stime.tv_sec,
274 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
275 break;
276 case 'T': /* System time. */
277 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
278 printf("%uh %um %02us",
279 (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
280 (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
281 (unsigned)(resp->ru.ru_stime.tv_sec % 60));
282 else
283 printf("%um %u.%02us", /* -> m:s. */
284 (unsigned)(resp->ru.ru_stime.tv_sec / 60),
285 (unsigned)(resp->ru.ru_stime.tv_sec % 60),
286 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
287 break;
288 case 'U': /* User time. */
289 printf("%u.%02u",
290 (unsigned)resp->ru.ru_utime.tv_sec,
291 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
292 break;
293 case 'u': /* User time. */
294 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
295 printf("%uh %um %02us",
296 (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
297 (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
298 (unsigned)(resp->ru.ru_utime.tv_sec % 60));
299 else
300 printf("%um %u.%02us", /* -> m:s. */
301 (unsigned)(resp->ru.ru_utime.tv_sec / 60),
302 (unsigned)(resp->ru.ru_utime.tv_sec % 60),
303 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
304 break;
305 case 'W': /* Times swapped out. */
306 printf("%lu", resp->ru.ru_nswap);
307 break;
308 case 'X': /* Average shared text size. */
309 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
310 break;
311 case 'Z': /* Page size. */
312 printf("%u", pagesize);
313 break;
314 case 'c': /* Involuntary context switches. */
315 printf("%lu", resp->ru.ru_nivcsw);
316 break;
317 case 'e': /* Elapsed real time in seconds. */
318 printf("%u.%02u",
319 (unsigned)resp->elapsed_ms / 1000,
320 (unsigned)(resp->elapsed_ms / 10) % 100);
321 break;
322 case 'k': /* Signals delivered. */
323 printf("%lu", resp->ru.ru_nsignals);
324 break;
325 case 'p': /* Average stack segment. */
326 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
327 break;
328 case 'r': /* Incoming socket messages received. */
329 printf("%lu", resp->ru.ru_msgrcv);
330 break;
331 case 's': /* Outgoing socket messages sent. */
332 printf("%lu", resp->ru.ru_msgsnd);
333 break;
334 case 't': /* Average resident set size. */
335 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
336 break;
337 case 'w': /* Voluntary context switches. */
338 printf("%lu", resp->ru.ru_nvcsw);
339 break;
340 case 'x': /* Exit status. */
341 printf("%u", WEXITSTATUS(resp->waitstatus));
342 break;
344 break;
346 #ifdef NOT_NEEDED_YET
347 case '\\': /* Format escape. */
348 switch (*++fmt) {
349 default:
350 bb_putchar('\\');
351 /*FALLTHROUGH*/
352 case '\\':
353 if (!*fmt) goto ret;
354 bb_putchar(*fmt);
355 break;
356 case 't':
357 bb_putchar('\t');
358 break;
359 case 'n':
360 bb_putchar('\n');
361 break;
363 break;
364 #endif
366 ++fmt;
368 /* ret: */
369 bb_putchar('\n');
372 /* Run command CMD and return statistics on it.
373 Put the statistics in *RESP. */
374 static void run_command(char *const *cmd, resource_t *resp)
376 pid_t pid;
377 void (*interrupt_signal)(int);
378 void (*quit_signal)(int);
380 resp->elapsed_ms = monotonic_ms();
381 pid = xvfork();
382 if (pid == 0) {
383 /* Child */
384 BB_EXECVP_or_die((char**)cmd);
387 /* Have signals kill the child but not self (if possible). */
388 //TODO: just block all sigs? and reenable them in the very end in main?
389 interrupt_signal = signal(SIGINT, SIG_IGN);
390 quit_signal = signal(SIGQUIT, SIG_IGN);
392 resuse_end(pid, resp);
394 /* Re-enable signals. */
395 signal(SIGINT, interrupt_signal);
396 signal(SIGQUIT, quit_signal);
399 int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
400 int time_main(int argc UNUSED_PARAM, char **argv)
402 resource_t res;
403 const char *output_format = default_format;
404 int opt;
406 opt_complementary = "-1"; /* at least one arg */
407 /* "+": stop on first non-option */
408 opt = getopt32(argv, "+vp");
409 argv += optind;
410 if (opt & 1)
411 output_format = long_format;
412 if (opt & 2)
413 output_format = posix_format;
415 run_command(argv, &res);
417 /* Cheat. printf's are shorter :) */
418 xdup2(STDERR_FILENO, STDOUT_FILENO);
419 summarize(output_format, argv, &res);
421 if (WIFSTOPPED(res.waitstatus))
422 return WSTOPSIG(res.waitstatus);
423 if (WIFSIGNALED(res.waitstatus))
424 return WTERMSIG(res.waitstatus);
425 if (WIFEXITED(res.waitstatus))
426 return WEXITSTATUS(res.waitstatus);
427 fflush_stdout_and_exit(EXIT_SUCCESS);