Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 532 - (show annotations) (download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 14430 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd

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 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 #include "busybox.h"
13
14 #define TV_MSEC tv_usec / 1000
15
16 /* Information on the resources used by a child process. */
17 typedef struct {
18 int waitstatus;
19 struct rusage ru;
20 struct timeval start, elapsed; /* Wallclock time of process. */
21 } resource_t;
22
23 /* msec = milliseconds = 1/1,000 (1*10e-3) second.
24 usec = microseconds = 1/1,000,000 (1*10e-6) second. */
25
26 #ifndef TICKS_PER_SEC
27 #define TICKS_PER_SEC 100
28 #endif
29
30 /* The number of milliseconds in one `tick' used by the `rusage' structure. */
31 #define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
32
33 /* Return the number of clock ticks that occur in M milliseconds. */
34 #define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
35
36 #define UL unsigned long
37
38 static const char *const default_format = "real\t%E\nuser\t%u\nsys\t%T";
39
40 /* The output format for the -p option .*/
41 static const char *const posix_format = "real %e\nuser %U\nsys %S";
42
43
44 /* Format string for printing all statistics verbosely.
45 Keep this output to 24 lines so users on terminals can see it all.*/
46 static const char *const long_format =
47 "\tCommand being timed: \"%C\"\n"
48 "\tUser time (seconds): %U\n"
49 "\tSystem time (seconds): %S\n"
50 "\tPercent of CPU this job got: %P\n"
51 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
52 "\tAverage shared text size (kbytes): %X\n"
53 "\tAverage unshared data size (kbytes): %D\n"
54 "\tAverage stack size (kbytes): %p\n"
55 "\tAverage total size (kbytes): %K\n"
56 "\tMaximum resident set size (kbytes): %M\n"
57 "\tAverage resident set size (kbytes): %t\n"
58 "\tMajor (requiring I/O) page faults: %F\n"
59 "\tMinor (reclaiming a frame) page faults: %R\n"
60 "\tVoluntary context switches: %w\n"
61 "\tInvoluntary context switches: %c\n"
62 "\tSwaps: %W\n"
63 "\tFile system inputs: %I\n"
64 "\tFile system outputs: %O\n"
65 "\tSocket messages sent: %s\n"
66 "\tSocket messages received: %r\n"
67 "\tSignals delivered: %k\n"
68 "\tPage size (bytes): %Z\n"
69 "\tExit status: %x";
70
71
72 /* Wait for and fill in data on child process PID.
73 Return 0 on error, 1 if ok. */
74
75 /* pid_t is short on BSDI, so don't try to promote it. */
76 static int resuse_end(pid_t pid, resource_t * resp)
77 {
78 int status;
79
80 pid_t caught;
81
82 /* Ignore signals, but don't ignore the children. When wait3
83 returns the child process, set the time the command finished. */
84 while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
85 if (caught == -1)
86 return 0;
87 }
88
89 gettimeofday(&resp->elapsed, (struct timezone *) 0);
90 resp->elapsed.tv_sec -= resp->start.tv_sec;
91 if (resp->elapsed.tv_usec < resp->start.tv_usec) {
92 /* Manually carry a one from the seconds field. */
93 resp->elapsed.tv_usec += 1000000;
94 --resp->elapsed.tv_sec;
95 }
96 resp->elapsed.tv_usec -= resp->start.tv_usec;
97
98 resp->waitstatus = status;
99
100 return 1;
101 }
102
103 /* Print ARGV, with each entry in ARGV separated by FILLER. */
104 static void printargv(char *const *argv, const char *filler)
105 {
106 fputs(*argv, stdout);
107 while (*++argv) {
108 fputs(filler, stdout);
109 fputs(*argv, stdout);
110 }
111 }
112
113 /* Return the number of kilobytes corresponding to a number of pages PAGES.
114 (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
115
116 Try to do arithmetic so that the risk of overflow errors is minimized.
117 This is funky since the pagesize could be less than 1K.
118 Note: Some machines express getrusage statistics in terms of K,
119 others in terms of pages. */
120
121 static unsigned long ptok(unsigned long pages)
122 {
123 static unsigned long ps;
124 unsigned long tmp;
125
126 /* Initialization. */
127 if (ps == 0)
128 ps = getpagesize();
129
130 /* Conversion. */
131 if (pages > (LONG_MAX / ps)) { /* Could overflow. */
132 tmp = pages / 1024; /* Smaller first, */
133 return tmp * ps; /* then larger. */
134 }
135 /* Could underflow. */
136 tmp = pages * ps; /* Larger first, */
137 return tmp / 1024; /* then smaller. */
138 }
139
140 /* summarize: Report on the system use of a command.
141
142 Print the FMT argument except that `%' sequences
143 have special meaning, and `\n' and `\t' are translated into
144 newline and tab, respectively, and `\\' is translated into `\'.
145
146 The character following a `%' can be:
147 (* means the tcsh time builtin also recognizes it)
148 % == a literal `%'
149 C == command name and arguments
150 * D == average unshared data size in K (ru_idrss+ru_isrss)
151 * E == elapsed real (wall clock) time in [hour:]min:sec
152 * F == major page faults (required physical I/O) (ru_majflt)
153 * I == file system inputs (ru_inblock)
154 * K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
155 * M == maximum resident set size in K (ru_maxrss)
156 * O == file system outputs (ru_oublock)
157 * P == percent of CPU this job got (total cpu time / elapsed time)
158 * R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
159 * S == system (kernel) time (seconds) (ru_stime)
160 * T == system time in [hour:]min:sec
161 * U == user time (seconds) (ru_utime)
162 * u == user time in [hour:]min:sec
163 * W == times swapped out (ru_nswap)
164 * X == average amount of shared text in K (ru_ixrss)
165 Z == page size
166 * c == involuntary context switches (ru_nivcsw)
167 e == elapsed real time in seconds
168 * k == signals delivered (ru_nsignals)
169 p == average unshared stack size in K (ru_isrss)
170 * r == socket messages received (ru_msgrcv)
171 * s == socket messages sent (ru_msgsnd)
172 t == average resident set size in K (ru_idrss)
173 * w == voluntary context switches (ru_nvcsw)
174 x == exit status of command
175
176 Various memory usages are found by converting from page-seconds
177 to kbytes by multiplying by the page size, dividing by 1024,
178 and dividing by elapsed real time.
179
180 FMT is the format string, interpreted as described above.
181 COMMAND is the command and args that are being summarized.
182 RESP is resource information on the command. */
183
184 static void summarize(const char *fmt, char **command, resource_t * resp)
185 {
186 unsigned long r; /* Elapsed real milliseconds. */
187 unsigned long v; /* Elapsed virtual (CPU) milliseconds. */
188
189 if (WIFSTOPPED(resp->waitstatus))
190 printf("Command stopped by signal %d\n",
191 WSTOPSIG(resp->waitstatus));
192 else if (WIFSIGNALED(resp->waitstatus))
193 printf("Command terminated by signal %d\n",
194 WTERMSIG(resp->waitstatus));
195 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
196 printf("Command exited with non-zero status %d\n",
197 WEXITSTATUS(resp->waitstatus));
198
199 /* Convert all times to milliseconds. Occasionally, one of these values
200 comes out as zero. Dividing by zero causes problems, so we first
201 check the time value. If it is zero, then we take `evasive action'
202 instead of calculating a value. */
203
204 r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
205
206 v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
207 resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
208
209 /* putchar() != putc(stdout) in glibc! */
210
211 while (*fmt) {
212 /* Handle leading literal part */
213 int n = strcspn(fmt, "%\\");
214 if (n) {
215 printf("%.*s", n, fmt);
216 fmt += n;
217 continue;
218 }
219
220 switch (*fmt) {
221 #ifdef NOT_NEEDED
222 /* Handle literal char */
223 /* Usually we optimize for size, but there is a limit
224 * for everything. With this we do a lot of 1-byte writes */
225 default:
226 putc(*fmt, stdout);
227 break;
228 #endif
229
230 case '%':
231 switch (*++fmt) {
232 #ifdef NOT_NEEDED_YET
233 /* Our format strings do not have these */
234 /* and we do not take format str from user */
235 default:
236 putc('%', stdout);
237 /*FALLTHROUGH*/
238 case '%':
239 if (!*fmt) goto ret;
240 putc(*fmt, stdout);
241 break;
242 #endif
243 case 'C': /* The command that got timed. */
244 printargv(command, " ");
245 break;
246 case 'D': /* Average unshared data size. */
247 printf("%lu",
248 MSEC_TO_TICKS(v) == 0 ? 0 :
249 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
250 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
251 break;
252 case 'E': /* Elapsed real (wall clock) time. */
253 if (resp->elapsed.tv_sec >= 3600) /* One hour -> h:m:s. */
254 printf("%ldh %ldm %02lds",
255 resp->elapsed.tv_sec / 3600,
256 (resp->elapsed.tv_sec % 3600) / 60,
257 resp->elapsed.tv_sec % 60);
258 else
259 printf("%ldm %ld.%02lds", /* -> m:s. */
260 resp->elapsed.tv_sec / 60,
261 resp->elapsed.tv_sec % 60,
262 resp->elapsed.tv_usec / 10000);
263 break;
264 case 'F': /* Major page faults. */
265 printf("%ld", resp->ru.ru_majflt);
266 break;
267 case 'I': /* Inputs. */
268 printf("%ld", resp->ru.ru_inblock);
269 break;
270 case 'K': /* Average mem usage == data+stack+text. */
271 printf("%lu",
272 MSEC_TO_TICKS(v) == 0 ? 0 :
273 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
274 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v) +
275 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
276 break;
277 case 'M': /* Maximum resident set size. */
278 printf("%lu", ptok((UL) resp->ru.ru_maxrss));
279 break;
280 case 'O': /* Outputs. */
281 printf("%ld", resp->ru.ru_oublock);
282 break;
283 case 'P': /* Percent of CPU this job got. */
284 /* % cpu is (total cpu time)/(elapsed time). */
285 if (r > 0)
286 printf("%lu%%", (v * 100 / r));
287 else
288 printf("?%%");
289 break;
290 case 'R': /* Minor page faults (reclaims). */
291 printf("%ld", resp->ru.ru_minflt);
292 break;
293 case 'S': /* System time. */
294 printf("%ld.%02ld",
295 resp->ru.ru_stime.tv_sec,
296 resp->ru.ru_stime.TV_MSEC / 10);
297 break;
298 case 'T': /* System time. */
299 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */
300 printf("%ldh %ldm %02lds",
301 resp->ru.ru_stime.tv_sec / 3600,
302 (resp->ru.ru_stime.tv_sec % 3600) / 60,
303 resp->ru.ru_stime.tv_sec % 60);
304 else
305 printf("%ldm %ld.%02lds", /* -> m:s. */
306 resp->ru.ru_stime.tv_sec / 60,
307 resp->ru.ru_stime.tv_sec % 60,
308 resp->ru.ru_stime.tv_usec / 10000);
309 break;
310 case 'U': /* User time. */
311 printf("%ld.%02ld",
312 resp->ru.ru_utime.tv_sec,
313 resp->ru.ru_utime.TV_MSEC / 10);
314 break;
315 case 'u': /* User time. */
316 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */
317 printf("%ldh %ldm %02lds",
318 resp->ru.ru_utime.tv_sec / 3600,
319 (resp->ru.ru_utime.tv_sec % 3600) / 60,
320 resp->ru.ru_utime.tv_sec % 60);
321 else
322 printf("%ldm %ld.%02lds", /* -> m:s. */
323 resp->ru.ru_utime.tv_sec / 60,
324 resp->ru.ru_utime.tv_sec % 60,
325 resp->ru.ru_utime.tv_usec / 10000);
326 break;
327 case 'W': /* Times swapped out. */
328 printf("%ld", resp->ru.ru_nswap);
329 break;
330 case 'X': /* Average shared text size. */
331 printf("%lu",
332 MSEC_TO_TICKS(v) == 0 ? 0 :
333 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
334 break;
335 case 'Z': /* Page size. */
336 printf("%d", getpagesize());
337 break;
338 case 'c': /* Involuntary context switches. */
339 printf("%ld", resp->ru.ru_nivcsw);
340 break;
341 case 'e': /* Elapsed real time in seconds. */
342 printf("%ld.%02ld",
343 resp->elapsed.tv_sec, resp->elapsed.tv_usec / 10000);
344 break;
345 case 'k': /* Signals delivered. */
346 printf("%ld", resp->ru.ru_nsignals);
347 break;
348 case 'p': /* Average stack segment. */
349 printf("%lu",
350 MSEC_TO_TICKS(v) == 0 ? 0 :
351 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
352 break;
353 case 'r': /* Incoming socket messages received. */
354 printf("%ld", resp->ru.ru_msgrcv);
355 break;
356 case 's': /* Outgoing socket messages sent. */
357 printf("%ld", resp->ru.ru_msgsnd);
358 break;
359 case 't': /* Average resident set size. */
360 printf("%lu",
361 MSEC_TO_TICKS(v) == 0 ? 0 :
362 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v));
363 break;
364 case 'w': /* Voluntary context switches. */
365 printf("%ld", resp->ru.ru_nvcsw);
366 break;
367 case 'x': /* Exit status. */
368 printf("%d", WEXITSTATUS(resp->waitstatus));
369 break;
370 }
371 break;
372
373 #ifdef NOT_NEEDED_YET
374 case '\\': /* Format escape. */
375 switch (*++fmt) {
376 default:
377 putc('\\', stdout);
378 /*FALLTHROUGH*/
379 case '\\':
380 if (!*fmt) goto ret;
381 putc(*fmt, stdout);
382 break;
383 case 't':
384 putc('\t', stdout);
385 break;
386 case 'n':
387 putc('\n', stdout);
388 break;
389 }
390 break;
391 #endif
392 }
393 ++fmt;
394 }
395 /* ret: */
396 putc('\n', stdout);
397 }
398
399 /* Run command CMD and return statistics on it.
400 Put the statistics in *RESP. */
401 static void run_command(char *const *cmd, resource_t * resp)
402 {
403 pid_t pid; /* Pid of child. */
404 __sighandler_t interrupt_signal, quit_signal;
405
406 gettimeofday(&resp->start, (struct timezone *) 0);
407 pid = vfork(); /* Run CMD as child process. */
408 if (pid < 0)
409 bb_error_msg_and_die("cannot fork");
410 else if (pid == 0) { /* If child. */
411 /* Don't cast execvp arguments; that causes errors on some systems,
412 versus merely warnings if the cast is left off. */
413 execvp(cmd[0], cmd);
414 bb_error_msg("cannot run %s", cmd[0]);
415 _exit(errno == ENOENT ? 127 : 126);
416 }
417
418 /* Have signals kill the child but not self (if possible). */
419 interrupt_signal = signal(SIGINT, SIG_IGN);
420 quit_signal = signal(SIGQUIT, SIG_IGN);
421
422 if (resuse_end(pid, resp) == 0)
423 bb_error_msg("error waiting for child process");
424
425 /* Re-enable signals. */
426 signal(SIGINT, interrupt_signal);
427 signal(SIGQUIT, quit_signal);
428 }
429
430 int time_main(int argc, char **argv)
431 {
432 resource_t res;
433 const char *output_format = default_format;
434 char c;
435
436 goto next;
437 /* Parse any options -- don't use getopt() here so we don't
438 * consume the args of our client application... */
439 while (argc > 0 && argv[0][0] == '-') {
440 while ((c = *++*argv)) {
441 switch (c) {
442 case 'v':
443 output_format = long_format;
444 break;
445 case 'p':
446 output_format = posix_format;
447 break;
448 default:
449 bb_show_usage();
450 }
451 }
452 next:
453 argv++;
454 argc--;
455 if (!argc)
456 bb_show_usage();
457 }
458
459 run_command(argv, &res);
460
461 /* Cheat. printf's are shorter :) */
462 stdout = stderr;
463 dup2(2, 1); /* just in case libc does something silly :( */
464 summarize(output_format, argv, &res);
465
466 if (WIFSTOPPED(res.waitstatus))
467 return WSTOPSIG(res.waitstatus);
468 if (WIFSIGNALED(res.waitstatus))
469 return WTERMSIG(res.waitstatus);
470 if (WIFEXITED(res.waitstatus))
471 return WEXITSTATUS(res.waitstatus);
472 fflush_stdout_and_exit(0);
473 }