ps: add conditional support for -o [e]time
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 5 Jan 2008 03:26:41 +0000 (03:26 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 5 Jan 2008 03:26:41 +0000 (03:26 -0000)
applets/applet_tables.c
include/libbb.h
libbb/procps.c
procps/Config.in
procps/ps.c

index 3945f7c..3353f36 100644 (file)
@@ -71,7 +71,7 @@ int main(int argc, char **argv)
 
        puts("/* This is a generated file, don't edit */");
 
-       puts("const char applet_names[] ALIGN1 = \"\" \n");
+       puts("const char applet_names[] ALIGN1 = \"\"\n");
        for (i = 0; i < NUM_APPLETS; i++) {
                printf("\"%s\" \"\\0\"\n", applets[i].name);
        }
index fef8fe2..33e73b2 100644 (file)
@@ -990,6 +990,7 @@ typedef struct procps_status_t {
         * it is memset(0) for each process in procps_scan() */
        unsigned long vsz, rss; /* we round it to kbytes */
        unsigned long stime, utime;
+       unsigned long start_time;
        unsigned pid;
        unsigned ppid;
        unsigned pgid;
@@ -1032,11 +1033,12 @@ enum {
        PSSCAN_SMAPS    = (1 << 15) * ENABLE_FEATURE_TOPMEM,
        PSSCAN_ARGVN    = (1 << 16) * (ENABLE_PGREP | ENABLE_PKILL),
        USE_SELINUX(PSSCAN_CONTEXT = 1 << 17,)
+       PSSCAN_START_TIME = 1 << 18,
        /* These are all retrieved from proc/NN/stat in one go: */
        PSSCAN_STAT     = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
                        | PSSCAN_COMM | PSSCAN_STATE
                        | PSSCAN_VSZ | PSSCAN_RSS
-                       | PSSCAN_STIME | PSSCAN_UTIME
+                       | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
                        | PSSCAN_TTY,
 };
 procps_status_t* alloc_procps_scan(int flags);
index 6bc16d1..015ad80 100644 (file)
@@ -243,7 +243,8 @@ procps_status_t *procps_scan(procps_status_t* sp, int flags)
                                "%lu %lu "             /* utime, stime */
                                "%*s %*s %*s "         /* cutime, cstime, priority */
                                "%ld "                 /* nice */
-                               "%*s %*s %*s "         /* timeout, it_real_value, start_time */
+                               "%*s %*s "             /* timeout, it_real_value */
+                               "%lu "                 /* start_time */
                                "%lu "                 /* vsize */
                                "%lu "                 /* rss */
                        /*      "%lu %lu %lu %lu %lu %lu " rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
@@ -254,6 +255,7 @@ procps_status_t *procps_scan(procps_status_t* sp, int flags)
                                &sp->pgid, &sp->sid, &tty,
                                &sp->utime, &sp->stime,
                                &tasknice,
+                               &sp->start_time,
                                &vsz,
                                &rss);
                        if (n != 10)
@@ -280,7 +282,8 @@ procps_status_t *procps_scan(procps_status_t* sp, int flags)
                        sp->stime = fast_strtoul_10(&cp);
                        cp = skip_fields(cp, 3); /* cutime, cstime, priority */
                        tasknice = fast_strtoul_10(&cp);
-                       cp = skip_fields(cp, 3); /* timeout, it_real_value, start_time */
+                       cp = skip_fields(cp, 2); /* timeout, it_real_value */
+                       sp->start_time = fast_strtoul_10(&cp);
                        /* vsz is in bytes and we want kb */
                        sp->vsz = fast_strtoul_10(&cp) >> 10;
                        /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
index 7e7ee79..585893e 100644 (file)
@@ -99,6 +99,21 @@ config FEATURE_PS_WIDE
          If given once, 132 chars are printed and given more than
          one, the length is unlimited.
 
+config FEATURE_PS_TIME
+       bool "Enable time and elapsed time output"
+       default n
+       depends on PS && DESKTOP
+       help
+         Support -o time and -o etime output specifiers.
+
+config FEATURE_PS_UNUSUAL_SYSTEMS
+       bool "Support Linux prior to 2.4.0 and non-ELF systems"
+       default n
+       depends on FEATURE_PS_TIME
+       help
+         Include support for measuring HZ on old kernels and non-ELF systems
+         (if you are on Linux 2.4.0+ and use ELF, you don't need this)
+
 config RENICE
        bool "renice"
        default n
index 08922eb..a6c35f1 100644 (file)
@@ -16,6 +16,143 @@ enum { MAX_WIDTH = 2*1024 };
 
 #if ENABLE_DESKTOP
 
+#include <sys/times.h> /* for times() */
+//#include <sys/sysinfo.h> /* for sysinfo() */
+#ifndef AT_CLKTCK
+#define AT_CLKTCK 17
+#endif
+
+
+#if ENABLE_SELINUX
+#define SELINIX_O_PREFIX "label,"
+#define DEFAULT_O_STR    (SELINIX_O_PREFIX "pid,user" USE_FEATURE_PS_TIME(",time"))
+#else
+#define DEFAULT_O_STR    ("pid,user" USE_FEATURE_PS_TIME(",time"))
+#endif
+
+typedef struct {
+       uint16_t width;
+       char name[6];
+       const char *header;
+       void (*f)(char *buf, int size, const procps_status_t *ps);
+       int ps_flags;
+} ps_out_t;
+
+struct globals {
+       ps_out_t* out;
+       int out_cnt;
+       int print_header;
+       int need_flags;
+       char *buffer;
+       unsigned terminal_width;
+#if ENABLE_FEATURE_PS_TIME
+       unsigned kernel_HZ;
+       unsigned long long seconds_since_boot;
+#endif
+       char default_o[sizeof(DEFAULT_O_STR)];
+};
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define out                (G.out               )
+#define out_cnt            (G.out_cnt           )
+#define print_header       (G.print_header      )
+#define need_flags         (G.need_flags        )
+#define buffer             (G.buffer            )
+#define terminal_width     (G.terminal_width    )
+#define kernel_HZ          (G.kernel_HZ         )
+#define seconds_since_boot (G.seconds_since_boot)
+#define default_o          (G.default_o         )
+
+#if ENABLE_FEATURE_PS_TIME
+/* for ELF executables, notes are pushed before environment and args */
+static ptrdiff_t find_elf_note(ptrdiff_t findme)
+{
+        ptrdiff_t *ep = (ptrdiff_t *) environ;
+
+        while (*ep++);
+        while (*ep) {
+                if (ep[0] == findme) {
+                        return ep[1];
+                }
+                ep += 2;
+        }
+        return -1;
+}
+
+#if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS
+static unsigned get_HZ_by_waiting(void)
+{
+        struct timeval tv1, tv2;
+        unsigned t1, t2, r, hz;
+        unsigned cnt = cnt; /* for compiler */
+        int diff;
+
+        r = 0;
+
+        /* Wait for times() to reach new tick */
+        t1 = times(NULL);
+        do {
+                t2 = times(NULL);
+        } while (t2 == t1);
+        gettimeofday(&tv2, NULL);
+
+        do {
+                t1 = t2;
+                tv1.tv_usec = tv2.tv_usec;
+
+                /* Wait exactly one times() tick */
+                do {
+                        t2 = times(NULL);
+                } while (t2 == t1);
+                gettimeofday(&tv2, NULL);
+
+                /* Calculate ticks per sec, rounding up to even */
+                diff = tv2.tv_usec - tv1.tv_usec;
+                if (diff <= 0) diff += 1000000;
+                hz = 1000000u / (unsigned)diff;
+                hz = (hz+1) & ~1;
+
+               /* Count how many same hz values we saw */
+                if (r != hz) {
+                        r = hz;
+                        cnt = 0;
+                }
+                cnt++;
+        } while (cnt < 3); /* exit if saw 3 same values */
+
+        return r;
+}
+#else
+static inline unsigned get_HZ_by_waiting(void)
+{
+       /* Better method? */
+       return 100;
+}
+#endif
+
+static unsigned get_kernel_HZ(void)
+{
+       //char buf[64];
+       struct sysinfo info;
+
+       if (kernel_HZ)
+               return kernel_HZ;
+
+       /* Works for ELF only, Linux 2.4.0+ */
+       kernel_HZ = find_elf_note(AT_CLKTCK);
+       if (kernel_HZ == (unsigned)-1)
+               kernel_HZ = get_HZ_by_waiting();
+
+       //if (open_read_close("/proc/uptime", buf, sizeof(buf) <= 0)
+       //      bb_perror_msg_and_die("cannot read %s", "/proc/uptime");
+       //buf[sizeof(buf)-1] = '\0';
+       ///sscanf(buf, "%llu", &seconds_since_boot);
+       sysinfo(&info);
+       seconds_since_boot = info.uptime;
+
+       return kernel_HZ;
+}
+#endif
+
 /* Print value to buf, max size+1 chars (including trailing '\0') */
 
 static void func_user(char *buf, int size, const procps_status_t *ps)
@@ -73,6 +210,34 @@ static void func_tty(char *buf, int size, const procps_status_t *ps)
                snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
 }
 
+#if ENABLE_FEATURE_PS_TIME
+static void func_etime(char *buf, int size, const procps_status_t *ps)
+{
+       /* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */
+       unsigned long mm;
+       unsigned ss;
+
+       mm = ps->start_time / get_kernel_HZ();
+       /* must be after get_kernel_HZ()! */
+       mm = seconds_since_boot - mm;
+       ss = mm % 60;
+       mm /= 60;
+       snprintf(buf, size+1, "%3lu:%02u", mm, ss);
+}
+
+static void func_time(char *buf, int size, const procps_status_t *ps)
+{
+       /* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */
+       unsigned long mm;
+       unsigned ss;
+
+       mm = (ps->utime + ps->stime) / get_kernel_HZ();
+       ss = mm % 60;
+       mm /= 60;
+       snprintf(buf, size+1, "%3lu:%02u", mm, ss);
+}
+#endif
+
 #if ENABLE_SELINUX
 static void func_label(char *buf, int size, const procps_status_t *ps)
 {
@@ -86,29 +251,11 @@ static void func_nice(char *buf, int size, const procps_status_t *ps)
        ps->???
 }
 
-static void func_etime(char *buf, int size, const procps_status_t *ps)
-{
-       elapled time [[dd-]hh:]mm:ss
-}
-
-static void func_time(char *buf, int size, const procps_status_t *ps)
-{
-       cumulative time [[dd-]hh:]mm:ss
-}
-
 static void func_pcpu(char *buf, int size, const procps_status_t *ps)
 {
 }
 */
 
-typedef struct {
-       uint16_t width;
-       char name[6];
-       const char *header;
-       void (*f)(char *buf, int size, const procps_status_t *ps);
-       int ps_flags;
-} ps_out_t;
-
 static const ps_out_t out_spec[] = {
 // Mandated by POSIX:
        { 8                  , "user"  ,"USER"   ,func_user  ,PSSCAN_UIDGID  },
@@ -117,13 +264,17 @@ static const ps_out_t out_spec[] = {
        { 5                  , "pid"   ,"PID"    ,func_pid   ,PSSCAN_PID     },
        { 5                  , "ppid"  ,"PPID"   ,func_ppid  ,PSSCAN_PPID    },
        { 5                  , "pgid"  ,"PGID"   ,func_pgid  ,PSSCAN_PGID    },
-//     { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_        },
+#if ENABLE_FEATURE_PS_TIME
+       { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME },
+#endif
 //     { sizeof("GROUP"  )-1, "group" ,"GROUP"  ,func_group ,PSSCAN_UIDGID  },
 //     { sizeof("NI"     )-1, "nice"  ,"NI"     ,func_nice  ,PSSCAN_        },
 //     { sizeof("%CPU"   )-1, "pcpu"  ,"%CPU"   ,func_pcpu  ,PSSCAN_        },
 //     { sizeof("RGROUP" )-1, "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID  },
 //     { sizeof("RUSER"  )-1, "ruser" ,"RUSER"  ,func_ruser ,PSSCAN_UIDGID  },
-//     { sizeof("TIME"   )-1, "time"  ,"TIME"   ,func_time  ,PSSCAN_        },
+#if ENABLE_FEATURE_PS_TIME
+       { 6                  , "time"  ,"TIME"   ,func_time  ,PSSCAN_STIME | PSSCAN_UTIME },
+#endif
        { 6                  , "tty"   ,"TT"     ,func_tty   ,PSSCAN_TTY     },
        { 4                  , "vsz"   ,"VSZ"    ,func_vsz   ,PSSCAN_VSZ     },
 // Not mandated by POSIX, but useful:
@@ -133,31 +284,6 @@ static const ps_out_t out_spec[] = {
 #endif
 };
 
-#if ENABLE_SELINUX
-#define SELINIX_O_PREFIX "label,"
-#define DEFAULT_O_STR    SELINIX_O_PREFIX "pid,user" /* TODO: ,vsz,stat */ ",args"
-#else
-#define DEFAULT_O_STR    "pid,user" /* TODO: ,vsz,stat */ ",args"
-#endif
-
-struct globals {
-       ps_out_t* out;
-       int out_cnt;
-       int print_header;
-       int need_flags;
-       char *buffer;
-       unsigned terminal_width;
-       char default_o[sizeof(DEFAULT_O_STR)];
-};
-#define G (*(struct globals*)&bb_common_bufsiz1)
-#define out            (G.out           )
-#define out_cnt        (G.out_cnt       )
-#define print_header   (G.print_header  )
-#define need_flags     (G.need_flags    )
-#define buffer         (G.buffer        )
-#define terminal_width (G.terminal_width)
-#define default_o      (G.default_o     )
-
 static ps_out_t* new_out_t(void)
 {
        int i = out_cnt++;