source: branches/stable/mindi-busybox/miscutils/time.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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