Bump to version 1.22.1
[platform/upstream/busybox.git] / miscutils / time.c
index 705123c..19b0b44 100644 (file)
@@ -1,49 +1,43 @@
 /* vi: set sw=4 ts=4: */
-/* `time' utility to display resource usage of processes.
+/* 'time' utility to display resource usage of processes.
    Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
 
-   Licensed under GPL version 2, see file LICENSE in this tarball for details.
+   Licensed under GPLv2, see file LICENSE in this source tree.
 */
 /* Originally written by David Keppel <pardo@cs.washington.edu>.
    Heavily modified by David MacKenzie <djm@gnu.ai.mit.edu>.
    Heavily modified for busybox by Erik Andersen <andersen@codepoet.org>
 */
 
-#include "libbb.h"
+//usage:#define time_trivial_usage
+//usage:       "[-v] PROG ARGS"
+//usage:#define time_full_usage "\n\n"
+//usage:       "Run PROG, display resource usage when it exits\n"
+//usage:     "\n       -v      Verbose"
 
-#define TV_MSEC tv_usec / 1000
+#include "libbb.h"
+#include <sys/resource.h> /* getrusage */
 
 /* Information on the resources used by a child process.  */
 typedef struct {
        int waitstatus;
        struct rusage ru;
-       struct timeval start, elapsed;  /* Wallclock time of process.  */
+       unsigned elapsed_ms;    /* Wallclock time of process.  */
 } resource_t;
 
 /* msec = milliseconds = 1/1,000 (1*10e-3) second.
    usec = microseconds = 1/1,000,000 (1*10e-6) second.  */
 
-#ifndef TICKS_PER_SEC
-#define TICKS_PER_SEC 100
-#endif
-
-/* The number of milliseconds in one `tick' used by the `rusage' structure.  */
-#define MSEC_PER_TICK (1000 / TICKS_PER_SEC)
-
-/* Return the number of clock ticks that occur in M milliseconds.  */
-#define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)
-
 #define UL unsigned long
 
-static const char default_format[] = "real\t%E\nuser\t%u\nsys\t%T";
+static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
 
 /* The output format for the -p option .*/
-static const char posix_format[] = "real %e\nuser %U\nsys %S";
-
+static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
 
 /* Format string for printing all statistics verbosely.
    Keep this output to 24 lines so users on terminals can see it all.*/
-static const char long_format[] =
+static const char long_format[] ALIGN1 =
        "\tCommand being timed: \"%C\"\n"
        "\tUser time (seconds): %U\n"
        "\tSystem time (seconds): %S\n"
@@ -68,46 +62,31 @@ static const char long_format[] =
        "\tPage size (bytes): %Z\n"
        "\tExit status: %x";
 
-
 /* Wait for and fill in data on child process PID.
    Return 0 on error, 1 if ok.  */
-
 /* pid_t is short on BSDI, so don't try to promote it.  */
-static int resuse_end(pid_t pid, resource_t * resp)
+static void resuse_end(pid_t pid, resource_t *resp)
 {
-       int status;
-
        pid_t caught;
 
        /* Ignore signals, but don't ignore the children.  When wait3
-          returns the child process, set the time the command finished. */
-       while ((caught = wait3(&status, 0, &resp->ru)) != pid) {
-               if (caught == -1)
-                       return 0;
-       }
-
-       gettimeofday(&resp->elapsed, (struct timezone *) 0);
-       resp->elapsed.tv_sec -= resp->start.tv_sec;
-       if (resp->elapsed.tv_usec < resp->start.tv_usec) {
-               /* Manually carry a one from the seconds field.  */
-               resp->elapsed.tv_usec += 1000000;
-               --resp->elapsed.tv_sec;
+        * returns the child process, set the time the command finished. */
+       while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
+               if (caught == -1 && errno != EINTR) {
+                       bb_perror_msg("wait");
+                       return;
+               }
        }
-       resp->elapsed.tv_usec -= resp->start.tv_usec;
-
-       resp->waitstatus = status;
-
-       return 1;
+       resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
 }
 
-/* Print ARGV, with each entry in ARGV separated by FILLER.  */
-static void printargv(char *const *argv, const char *filler)
+static void printargv(char *const *argv)
 {
-       fputs(*argv, stdout);
-       while (*++argv) {
-               fputs(filler, stdout);
-               fputs(*argv, stdout);
-       }
+       const char *fmt = " %s" + 1;
+       do {
+               printf(fmt, *argv);
+               fmt = " %s";
+       } while (*++argv);
 }
 
 /* Return the number of kilobytes corresponding to a number of pages PAGES.
@@ -117,24 +96,18 @@ static void printargv(char *const *argv, const char *filler)
    This is funky since the pagesize could be less than 1K.
    Note: Some machines express getrusage statistics in terms of K,
    others in terms of pages.  */
-
-static unsigned long ptok(unsigned long pages)
+static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
 {
-       static unsigned long ps;
        unsigned long tmp;
 
-       /* Initialization.  */
-       if (ps == 0)
-               ps = getpagesize();
-
        /* Conversion.  */
-       if (pages > (LONG_MAX / ps)) {  /* Could overflow.  */
-               tmp = pages / 1024;     /* Smaller first, */
-               return tmp * ps;        /* then larger.  */
+       if (pages > (LONG_MAX / pagesize)) { /* Could overflow.  */
+               tmp = pages / 1024;     /* Smaller first, */
+               return tmp * pagesize;  /* then larger.  */
        }
        /* Could underflow.  */
-       tmp = pages * ps;       /* Larger first, */
-       return tmp / 1024;      /* then smaller.  */
+       tmp = pages * pagesize; /* Larger first, */
+       return tmp / 1024;      /* then smaller.  */
 }
 
 /* summarize: Report on the system use of a command.
@@ -181,32 +154,38 @@ static unsigned long ptok(unsigned long pages)
    COMMAND is the command and args that are being summarized.
    RESP is resource information on the command.  */
 
-static void summarize(const char *fmt, char **command, resource_t * resp)
+#ifndef TICKS_PER_SEC
+#define TICKS_PER_SEC 100
+#endif
+
+static void summarize(const char *fmt, char **command, resource_t *resp)
 {
-       unsigned long r;        /* Elapsed real milliseconds.  */
-       unsigned long v;        /* Elapsed virtual (CPU) milliseconds.  */
+       unsigned vv_ms;     /* Elapsed virtual (CPU) milliseconds */
+       unsigned cpu_ticks; /* Same, in "CPU ticks" */
+       unsigned pagesize = getpagesize();
 
+       /* Impossible: we do not use WUNTRACED flag in wait()...
        if (WIFSTOPPED(resp->waitstatus))
-               printf("Command stopped by signal %d\n",
+               printf("Command stopped by signal %u\n",
                                WSTOPSIG(resp->waitstatus));
-       else if (WIFSIGNALED(resp->waitstatus))
-               printf("Command terminated by signal %d\n",
+       else */
+       if (WIFSIGNALED(resp->waitstatus))
+               printf("Command terminated by signal %u\n",
                                WTERMSIG(resp->waitstatus));
        else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
-               printf("Command exited with non-zero status %d\n",
+               printf("Command exited with non-zero status %u\n",
                                WEXITSTATUS(resp->waitstatus));
 
-       /* Convert all times to milliseconds.  Occasionally, one of these values
-          comes out as zero.  Dividing by zero causes problems, so we first
-          check the time value.  If it is zero, then we take `evasive action'
-          instead of calculating a value.  */
-
-       r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000;
+       vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
+             + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
 
-       v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC +
-               resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC;
-
-       /* putchar() != putc(stdout) in glibc! */
+#if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
+       /* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
+       cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
+#else
+       cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
+#endif
+       if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
 
        while (*fmt) {
                /* Handle leading literal part */
@@ -223,7 +202,7 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                /* Usually we optimize for size, but there is a limit
                 * for everything. With this we do a lot of 1-byte writes */
                default:
-                       putc(*fmt, stdout);
+                       bb_putchar(*fmt);
                        break;
 #endif
 
@@ -233,139 +212,134 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                /* Our format strings do not have these */
                /* and we do not take format str from user */
                        default:
-                               putc('%', stdout);
+                               bb_putchar('%');
                                /*FALLTHROUGH*/
                        case '%':
                                if (!*fmt) goto ret;
-                               putc(*fmt, stdout);
+                               bb_putchar(*fmt);
                                break;
 #endif
                        case 'C':       /* The command that got timed.  */
-                               printargv(command, " ");
+                               printargv(command);
                                break;
                        case 'D':       /* Average unshared data size.  */
                                printf("%lu",
-                                               MSEC_TO_TICKS(v) == 0 ? 0 :
-                                               ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
-                                               ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
-                               break;
-                       case 'E':       /* Elapsed real (wall clock) time.  */
-                               if (resp->elapsed.tv_sec >= 3600)       /* One hour -> h:m:s.  */
-                                       printf("%ldh %ldm %02lds",
-                                                       resp->elapsed.tv_sec / 3600,
-                                                       (resp->elapsed.tv_sec % 3600) / 60,
-                                                       resp->elapsed.tv_sec % 60);
+                                       (ptok(pagesize, (UL) resp->ru.ru_idrss) +
+                                        ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
+                               break;
+                       case 'E': {     /* Elapsed real (wall clock) time.  */
+                               unsigned seconds = resp->elapsed_ms / 1000;
+                               if (seconds >= 3600)    /* One hour -> h:m:s.  */
+                                       printf("%uh %um %02us",
+                                                       seconds / 3600,
+                                                       (seconds % 3600) / 60,
+                                                       seconds % 60);
                                else
-                                       printf("%ldm %ld.%02lds",       /* -> m:s.  */
-                                                       resp->elapsed.tv_sec / 60,
-                                                       resp->elapsed.tv_sec % 60,
-                                                       resp->elapsed.tv_usec / 10000);
+                                       printf("%um %u.%02us",  /* -> m:s.  */
+                                                       seconds / 60,
+                                                       seconds % 60,
+                                                       (unsigned)(resp->elapsed_ms / 10) % 100);
                                break;
+                       }
                        case 'F':       /* Major page faults.  */
-                               printf("%ld", resp->ru.ru_majflt);
+                               printf("%lu", resp->ru.ru_majflt);
                                break;
                        case 'I':       /* Inputs.  */
-                               printf("%ld", resp->ru.ru_inblock);
+                               printf("%lu", resp->ru.ru_inblock);
                                break;
                        case 'K':       /* Average mem usage == data+stack+text.  */
                                printf("%lu",
-                                               MSEC_TO_TICKS(v) == 0 ? 0 :
-                                               ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) +
-                                               ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v) +
-                                               ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
+                                       (ptok(pagesize, (UL) resp->ru.ru_idrss) +
+                                        ptok(pagesize, (UL) resp->ru.ru_isrss) +
+                                        ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
                                break;
                        case 'M':       /* Maximum resident set size.  */
-                               printf("%lu", ptok((UL) resp->ru.ru_maxrss));
+                               printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
                                break;
                        case 'O':       /* Outputs.  */
-                               printf("%ld", resp->ru.ru_oublock);
+                               printf("%lu", resp->ru.ru_oublock);
                                break;
                        case 'P':       /* Percent of CPU this job got.  */
                                /* % cpu is (total cpu time)/(elapsed time).  */
-                               if (r > 0)
-                                       printf("%lu%%", (v * 100 / r));
+                               if (resp->elapsed_ms > 0)
+                                       printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
                                else
                                        printf("?%%");
                                break;
                        case 'R':       /* Minor page faults (reclaims).  */
-                               printf("%ld", resp->ru.ru_minflt);
+                               printf("%lu", resp->ru.ru_minflt);
                                break;
                        case 'S':       /* System time.  */
-                               printf("%ld.%02ld",
-                                               resp->ru.ru_stime.tv_sec,
-                                               resp->ru.ru_stime.TV_MSEC / 10);
+                               printf("%u.%02u",
+                                               (unsigned)resp->ru.ru_stime.tv_sec,
+                                               (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
                                break;
                        case 'T':       /* System time.  */
                                if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s.  */
-                                       printf("%ldh %ldm %02lds",
-                                                       resp->ru.ru_stime.tv_sec / 3600,
-                                                       (resp->ru.ru_stime.tv_sec % 3600) / 60,
-                                                       resp->ru.ru_stime.tv_sec % 60);
+                                       printf("%uh %um %02us",
+                                                       (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
+                                                       (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
+                                                       (unsigned)(resp->ru.ru_stime.tv_sec % 60));
                                else
-                                       printf("%ldm %ld.%02lds",       /* -> m:s.  */
-                                                       resp->ru.ru_stime.tv_sec / 60,
-                                                       resp->ru.ru_stime.tv_sec % 60,
-                                                       resp->ru.ru_stime.tv_usec / 10000);
+                                       printf("%um %u.%02us",  /* -> m:s.  */
+                                                       (unsigned)(resp->ru.ru_stime.tv_sec / 60),
+                                                       (unsigned)(resp->ru.ru_stime.tv_sec % 60),
+                                                       (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
                                break;
                        case 'U':       /* User time.  */
-                               printf("%ld.%02ld",
-                                               resp->ru.ru_utime.tv_sec,
-                                               resp->ru.ru_utime.TV_MSEC / 10);
+                               printf("%u.%02u",
+                                               (unsigned)resp->ru.ru_utime.tv_sec,
+                                               (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
                                break;
                        case 'u':       /* User time.  */
                                if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s.  */
-                                       printf("%ldh %ldm %02lds",
-                                                       resp->ru.ru_utime.tv_sec / 3600,
-                                                       (resp->ru.ru_utime.tv_sec % 3600) / 60,
-                                                       resp->ru.ru_utime.tv_sec % 60);
+                                       printf("%uh %um %02us",
+                                                       (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
+                                                       (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
+                                                       (unsigned)(resp->ru.ru_utime.tv_sec % 60));
                                else
-                                       printf("%ldm %ld.%02lds",       /* -> m:s.  */
-                                                       resp->ru.ru_utime.tv_sec / 60,
-                                                       resp->ru.ru_utime.tv_sec % 60,
-                                                       resp->ru.ru_utime.tv_usec / 10000);
+                                       printf("%um %u.%02us",  /* -> m:s.  */
+                                                       (unsigned)(resp->ru.ru_utime.tv_sec / 60),
+                                                       (unsigned)(resp->ru.ru_utime.tv_sec % 60),
+                                                       (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
                                break;
                        case 'W':       /* Times swapped out.  */
-                               printf("%ld", resp->ru.ru_nswap);
+                               printf("%lu", resp->ru.ru_nswap);
                                break;
                        case 'X':       /* Average shared text size.  */
-                               printf("%lu",
-                                               MSEC_TO_TICKS(v) == 0 ? 0 :
-                                               ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v));
+                               printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
                                break;
                        case 'Z':       /* Page size.  */
-                               printf("%d", getpagesize());
+                               printf("%u", pagesize);
                                break;
                        case 'c':       /* Involuntary context switches.  */
-                               printf("%ld", resp->ru.ru_nivcsw);
+                               printf("%lu", resp->ru.ru_nivcsw);
                                break;
                        case 'e':       /* Elapsed real time in seconds.  */
-                               printf("%ld.%02ld",
-                                               resp->elapsed.tv_sec, resp->elapsed.tv_usec / 10000);
+                               printf("%u.%02u",
+                                               (unsigned)resp->elapsed_ms / 1000,
+                                               (unsigned)(resp->elapsed_ms / 10) % 100);
                                break;
                        case 'k':       /* Signals delivered.  */
-                               printf("%ld", resp->ru.ru_nsignals);
+                               printf("%lu", resp->ru.ru_nsignals);
                                break;
                        case 'p':       /* Average stack segment.  */
-                               printf("%lu",
-                                               MSEC_TO_TICKS(v) == 0 ? 0 :
-                                               ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));
+                               printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
                                break;
                        case 'r':       /* Incoming socket messages received.  */
-                               printf("%ld", resp->ru.ru_msgrcv);
+                               printf("%lu", resp->ru.ru_msgrcv);
                                break;
                        case 's':       /* Outgoing socket messages sent.  */
-                               printf("%ld", resp->ru.ru_msgsnd);
+                               printf("%lu", resp->ru.ru_msgsnd);
                                break;
                        case 't':       /* Average resident set size.  */
-                               printf("%lu",
-                                               MSEC_TO_TICKS(v) == 0 ? 0 :
-                                               ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v));
+                               printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
                                break;
                        case 'w':       /* Voluntary context switches.  */
-                               printf("%ld", resp->ru.ru_nvcsw);
+                               printf("%lu", resp->ru.ru_nvcsw);
                                break;
                        case 'x':       /* Exit status.  */
-                               printf("%d", WEXITSTATUS(resp->waitstatus));
+                               printf("%u", WEXITSTATUS(resp->waitstatus));
                                break;
                        }
                        break;
@@ -374,17 +348,17 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                case '\\':              /* Format escape.  */
                        switch (*++fmt) {
                        default:
-                               putc('\\', stdout);
+                               bb_putchar('\\');
                                /*FALLTHROUGH*/
                        case '\\':
                                if (!*fmt) goto ret;
-                               putc(*fmt, stdout);
+                               bb_putchar(*fmt);
                                break;
                        case 't':
-                               putc('\t', stdout);
+                               bb_putchar('\t');
                                break;
                        case 'n':
-                               putc('\n', stdout);
+                               bb_putchar('\n');
                                break;
                        }
                        break;
@@ -393,75 +367,56 @@ static void summarize(const char *fmt, char **command, resource_t * resp)
                ++fmt;
        }
  /* ret: */
-       putc('\n', stdout);
+       bb_putchar('\n');
 }
 
 /* Run command CMD and return statistics on it.
    Put the statistics in *RESP.  */
-static void run_command(char *const *cmd, resource_t * resp)
+static void run_command(char *const *cmd, resource_t *resp)
 {
-       pid_t pid;                      /* Pid of child.  */
-       __sighandler_t interrupt_signal, quit_signal;
-
-       gettimeofday(&resp->start, (struct timezone *) 0);
-       pid = vfork();          /* Run CMD as child process.  */
-       if (pid < 0)
-               bb_error_msg_and_die("cannot fork");
-       else if (pid == 0) {    /* If child.  */
-               /* Don't cast execvp arguments; that causes errors on some systems,
-                  versus merely warnings if the cast is left off.  */
-               BB_EXECVP(cmd[0], cmd);
-               bb_error_msg("cannot run %s", cmd[0]);
-               _exit(errno == ENOENT ? 127 : 126);
+       pid_t pid;
+       void (*interrupt_signal)(int);
+       void (*quit_signal)(int);
+
+       resp->elapsed_ms = monotonic_ms();
+       pid = xvfork();
+       if (pid == 0) {
+               /* Child */
+               BB_EXECVP_or_die((char**)cmd);
        }
 
        /* Have signals kill the child but not self (if possible).  */
+//TODO: just block all sigs? and reenable them in the very end in main?
        interrupt_signal = signal(SIGINT, SIG_IGN);
        quit_signal = signal(SIGQUIT, SIG_IGN);
 
-       if (resuse_end(pid, resp) == 0)
-               bb_error_msg("error waiting for child process");
+       resuse_end(pid, resp);
 
        /* Re-enable signals.  */
        signal(SIGINT, interrupt_signal);
        signal(SIGQUIT, quit_signal);
 }
 
-int time_main(int argc, char **argv);
-int time_main(int argc, char **argv)
+int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int time_main(int argc UNUSED_PARAM, char **argv)
 {
        resource_t res;
        const char *output_format = default_format;
-       char c;
-
-       goto next;
-       /* Parse any options  -- don't use getopt() here so we don't
-        * consume the args of our client application... */
-       while (argc > 0 && argv[0][0] == '-') {
-               while ((c = *++*argv)) {
-                       switch (c) {
-                       case 'v':
-                               output_format = long_format;
-                               break;
-                       case 'p':
-                               output_format = posix_format;
-                               break;
-                       default:
-                               bb_show_usage();
-                       }
-               }
- next:
-               argv++;
-               argc--;
-               if (!argc)
-                       bb_show_usage();
-       }
+       int opt;
+
+       opt_complementary = "-1"; /* at least one arg */
+       /* "+": stop on first non-option */
+       opt = getopt32(argv, "+vp");
+       argv += optind;
+       if (opt & 1)
+               output_format = long_format;
+       if (opt & 2)
+               output_format = posix_format;
 
        run_command(argv, &res);
 
        /* Cheat. printf's are shorter :) */
-       stdout = stderr;
-       dup2(2, 1); /* just in case libc does something silly :( */
+       xdup2(STDERR_FILENO, STDOUT_FILENO);
        summarize(output_format, argv, &res);
 
        if (WIFSTOPPED(res.waitstatus))
@@ -470,5 +425,5 @@ int time_main(int argc, char **argv)
                return WTERMSIG(res.waitstatus);
        if (WIFEXITED(res.waitstatus))
                return WEXITSTATUS(res.waitstatus);
-       fflush_stdout_and_exit(0);
+       fflush_stdout_and_exit(EXIT_SUCCESS);
 }