From 5fee2e1a79dc6fc05658821a86b0e7b5678a90dd Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sat, 5 Jan 2008 03:26:41 +0000 Subject: [PATCH] ps: add conditional support for -o [e]time --- applets/applet_tables.c | 2 +- include/libbb.h | 4 +- libbb/procps.c | 7 +- procps/Config.in | 15 +++ procps/ps.c | 216 +++++++++++++++++++++++++++++++--------- 5 files changed, 195 insertions(+), 49 deletions(-) diff --git a/applets/applet_tables.c b/applets/applet_tables.c index 3945f7c51..3353f3662 100644 --- a/applets/applet_tables.c +++ b/applets/applet_tables.c @@ -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); } diff --git a/include/libbb.h b/include/libbb.h index fef8fe2e0..33e73b27b 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -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); diff --git a/libbb/procps.c b/libbb/procps.c index 6bc16d166..015ad80ef 100644 --- a/libbb/procps.c +++ b/libbb/procps.c @@ -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? */ diff --git a/procps/Config.in b/procps/Config.in index 7e7ee7922..585893ed8 100644 --- a/procps/Config.in +++ b/procps/Config.in @@ -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 diff --git a/procps/ps.c b/procps/ps.c index 08922ebb6..a6c35f101 100644 --- a/procps/ps.c +++ b/procps/ps.c @@ -16,6 +16,143 @@ enum { MAX_WIDTH = 2*1024 }; #if ENABLE_DESKTOP +#include /* for times() */ +//#include /* 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++;