Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/miscutils/time.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 816 - (hide annotations) (download)
Fri Apr 24 18:33:46 2009 UTC (15 years, 1 month ago) by niro
File MIME type: text/plain
File size: 13799 byte(s)
-updated to busybox-1.13.4
1 niro 532 /* vi: set sw=4 ts=4: */
2 niro 816 /* 'time' utility to display resource usage of processes.
3 niro 532 Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
4    
5     Licensed under GPL version 2, see file LICENSE in this tarball for details.
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 niro 816 #include "libbb.h"
13 niro 532
14     /* Information on the resources used by a child process. */
15     typedef struct {
16     int waitstatus;
17     struct rusage ru;
18 niro 816 unsigned elapsed_ms; /* Wallclock time of process. */
19 niro 532 } 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    
26 niro 816 static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
27 niro 532
28     /* The output format for the -p option .*/
29 niro 816 static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
30 niro 532
31     /* Format string for printing all statistics verbosely.
32     Keep this output to 24 lines so users on terminals can see it all.*/
33 niro 816 static const char long_format[] ALIGN1 =
34 niro 532 "\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. */
61 niro 816 static void resuse_end(pid_t pid, resource_t *resp)
62 niro 532 {
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 niro 816 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
68     if (caught == -1 && errno != EINTR) {
69     bb_perror_msg("wait");
70     return;
71     }
72 niro 532 }
73 niro 816 resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms;
74 niro 532 }
75    
76 niro 816 static void printargv(char *const *argv)
77 niro 532 {
78 niro 816 const char *fmt = " %s" + 1;
79     do {
80     printf(fmt, *argv);
81     fmt = " %s";
82     } while (*++argv);
83 niro 532 }
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. */
92 niro 816 static unsigned long ptok(unsigned pagesize, unsigned long pages)
93 niro 532 {
94     unsigned long tmp;
95    
96     /* Conversion. */
97 niro 816 if (pages > (LONG_MAX / pagesize)) { /* Could overflow. */
98     tmp = pages / 1024; /* Smaller first, */
99     return tmp * pagesize; /* then larger. */
100 niro 532 }
101     /* Could underflow. */
102 niro 816 tmp = pages * pagesize; /* Larger first, */
103     return tmp / 1024; /* then smaller. */
104 niro 532 }
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 niro 816 #ifndef TICKS_PER_SEC
151     #define TICKS_PER_SEC 100
152     #endif
153    
154     static void summarize(const char *fmt, char **command, resource_t *resp)
155 niro 532 {
156 niro 816 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
157     unsigned cpu_ticks; /* Same, in "CPU ticks" */
158     unsigned pagesize = getpagesize();
159 niro 532
160 niro 816 /* Impossible: we do not use WUNTRACED flag in wait()...
161 niro 532 if (WIFSTOPPED(resp->waitstatus))
162 niro 816 printf("Command stopped by signal %u\n",
163 niro 532 WSTOPSIG(resp->waitstatus));
164 niro 816 else */
165     if (WIFSIGNALED(resp->waitstatus))
166     printf("Command terminated by signal %u\n",
167 niro 532 WTERMSIG(resp->waitstatus));
168     else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
169 niro 816 printf("Command exited with non-zero status %u\n",
170 niro 532 WEXITSTATUS(resp->waitstatus));
171    
172 niro 816 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 niro 532
175 niro 816 #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 niro 532
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 niro 816 bb_putchar(*fmt);
199 niro 532 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 niro 816 bb_putchar('%');
209 niro 532 /*FALLTHROUGH*/
210     case '%':
211     if (!*fmt) goto ret;
212 niro 816 bb_putchar(*fmt);
213 niro 532 break;
214     #endif
215     case 'C': /* The command that got timed. */
216 niro 816 printargv(command);
217 niro 532 break;
218     case 'D': /* Average unshared data size. */
219     printf("%lu",
220 niro 816 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
221     ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
222 niro 532 break;
223 niro 816 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 niro 532 else
231 niro 816 printf("%um %u.%02us", /* -> m:s. */
232     seconds / 60,
233     seconds % 60,
234     (unsigned)(resp->elapsed_ms / 10) % 100);
235 niro 532 break;
236 niro 816 }
237 niro 532 case 'F': /* Major page faults. */
238 niro 816 printf("%lu", resp->ru.ru_majflt);
239 niro 532 break;
240     case 'I': /* Inputs. */
241 niro 816 printf("%lu", resp->ru.ru_inblock);
242 niro 532 break;
243     case 'K': /* Average mem usage == data+stack+text. */
244     printf("%lu",
245 niro 816 (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 niro 532 break;
249     case 'M': /* Maximum resident set size. */
250 niro 816 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
251 niro 532 break;
252     case 'O': /* Outputs. */
253 niro 816 printf("%lu", resp->ru.ru_oublock);
254 niro 532 break;
255     case 'P': /* Percent of CPU this job got. */
256     /* % cpu is (total cpu time)/(elapsed time). */
257 niro 816 if (resp->elapsed_ms > 0)
258     printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
259 niro 532 else
260     printf("?%%");
261     break;
262     case 'R': /* Minor page faults (reclaims). */
263 niro 816 printf("%lu", resp->ru.ru_minflt);
264 niro 532 break;
265     case 'S': /* System time. */
266 niro 816 printf("%u.%02u",
267     (unsigned)resp->ru.ru_stime.tv_sec,
268     (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
269 niro 532 break;
270     case 'T': /* System time. */
271     if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
272 niro 816 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 niro 532 else
277 niro 816 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 niro 532 break;
282     case 'U': /* User time. */
283 niro 816 printf("%u.%02u",
284     (unsigned)resp->ru.ru_utime.tv_sec,
285     (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
286 niro 532 break;
287     case 'u': /* User time. */
288     if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
289 niro 816 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 niro 532 else
294 niro 816 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 niro 532 break;
299     case 'W': /* Times swapped out. */
300 niro 816 printf("%lu", resp->ru.ru_nswap);
301 niro 532 break;
302     case 'X': /* Average shared text size. */
303 niro 816 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
304 niro 532 break;
305     case 'Z': /* Page size. */
306 niro 816 printf("%u", getpagesize());
307 niro 532 break;
308     case 'c': /* Involuntary context switches. */
309 niro 816 printf("%lu", resp->ru.ru_nivcsw);
310 niro 532 break;
311     case 'e': /* Elapsed real time in seconds. */
312 niro 816 printf("%u.%02u",
313     (unsigned)resp->elapsed_ms / 1000,
314     (unsigned)(resp->elapsed_ms / 10) % 100);
315 niro 532 break;
316     case 'k': /* Signals delivered. */
317 niro 816 printf("%lu", resp->ru.ru_nsignals);
318 niro 532 break;
319     case 'p': /* Average stack segment. */
320 niro 816 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
321 niro 532 break;
322     case 'r': /* Incoming socket messages received. */
323 niro 816 printf("%lu", resp->ru.ru_msgrcv);
324 niro 532 break;
325     case 's': /* Outgoing socket messages sent. */
326 niro 816 printf("%lu", resp->ru.ru_msgsnd);
327 niro 532 break;
328     case 't': /* Average resident set size. */
329 niro 816 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
330 niro 532 break;
331     case 'w': /* Voluntary context switches. */
332 niro 816 printf("%lu", resp->ru.ru_nvcsw);
333 niro 532 break;
334     case 'x': /* Exit status. */
335 niro 816 printf("%u", WEXITSTATUS(resp->waitstatus));
336 niro 532 break;
337     }
338     break;
339    
340     #ifdef NOT_NEEDED_YET
341     case '\\': /* Format escape. */
342     switch (*++fmt) {
343     default:
344 niro 816 bb_putchar('\\');
345 niro 532 /*FALLTHROUGH*/
346     case '\\':
347     if (!*fmt) goto ret;
348 niro 816 bb_putchar(*fmt);
349 niro 532 break;
350     case 't':
351 niro 816 bb_putchar('\t');
352 niro 532 break;
353     case 'n':
354 niro 816 bb_putchar('\n');
355 niro 532 break;
356     }
357     break;
358     #endif
359     }
360     ++fmt;
361     }
362     /* ret: */
363 niro 816 bb_putchar('\n');
364 niro 532 }
365    
366     /* Run command CMD and return statistics on it.
367     Put the statistics in *RESP. */
368 niro 816 static void run_command(char *const *cmd, resource_t *resp)
369 niro 532 {
370     pid_t pid; /* Pid of child. */
371 niro 816 void (*interrupt_signal)(int);
372     void (*quit_signal)(int);
373 niro 532
374 niro 816 resp->elapsed_ms = monotonic_us() / 1000;
375 niro 532 pid = vfork(); /* Run CMD as child process. */
376     if (pid < 0)
377 niro 816 bb_perror_msg_and_die("fork");
378     if (pid == 0) { /* If child. */
379 niro 532 /* Don't cast execvp arguments; that causes errors on some systems,
380     versus merely warnings if the cast is left off. */
381 niro 816 BB_EXECVP(cmd[0], cmd);
382     xfunc_error_retval = (errno == ENOENT ? 127 : 126);
383     bb_error_msg_and_die("cannot run %s", cmd[0]);
384 niro 532 }
385    
386     /* Have signals kill the child but not self (if possible). */
387 niro 816 //TODO: just block all sigs? and reenable them in the very end in main?
388 niro 532 interrupt_signal = signal(SIGINT, SIG_IGN);
389     quit_signal = signal(SIGQUIT, SIG_IGN);
390    
391 niro 816 resuse_end(pid, resp);
392 niro 532
393     /* Re-enable signals. */
394     signal(SIGINT, interrupt_signal);
395     signal(SIGQUIT, quit_signal);
396     }
397    
398 niro 816 int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
399     int time_main(int argc UNUSED_PARAM, char **argv)
400 niro 532 {
401     resource_t res;
402     const char *output_format = default_format;
403 niro 816 int opt;
404 niro 532
405 niro 816 opt_complementary = "-1"; /* at least one arg */
406     /* "+": stop on first non-option */
407     opt = getopt32(argv, "+vp");
408     argv += optind;
409     if (opt & 1)
410     output_format = long_format;
411     if (opt & 2)
412     output_format = posix_format;
413 niro 532
414     run_command(argv, &res);
415    
416     /* Cheat. printf's are shorter :) */
417 niro 816 /* (but see bb_putchar() body for additional wrinkle!) */
418     xdup2(2, 1); /* just in case libc does something silly :( */
419 niro 532 stdout = stderr;
420     summarize(output_format, argv, &res);
421    
422     if (WIFSTOPPED(res.waitstatus))
423     return WSTOPSIG(res.waitstatus);
424     if (WIFSIGNALED(res.waitstatus))
425     return WTERMSIG(res.waitstatus);
426     if (WIFEXITED(res.waitstatus))
427     return WEXITSTATUS(res.waitstatus);
428 niro 816 fflush_stdout_and_exit(EXIT_SUCCESS);
429 niro 532 }