source: MondoRescue/branches/2.2.9/mindi-busybox/miscutils/time.c@ 3320

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