ps: add conditional support for -o [e]time

This commit is contained in:
Denis Vlasenko 2008-01-05 03:26:41 +00:00
parent 9cd30d30a0
commit 5fee2e1a79
5 changed files with 195 additions and 49 deletions

View File

@ -71,7 +71,7 @@ int main(int argc, char **argv)
puts("/* This is a generated file, don't edit */"); 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++) { for (i = 0; i < NUM_APPLETS; i++) {
printf("\"%s\" \"\\0\"\n", applets[i].name); printf("\"%s\" \"\\0\"\n", applets[i].name);
} }

View File

@ -990,6 +990,7 @@ typedef struct procps_status_t {
* it is memset(0) for each process in procps_scan() */ * it is memset(0) for each process in procps_scan() */
unsigned long vsz, rss; /* we round it to kbytes */ unsigned long vsz, rss; /* we round it to kbytes */
unsigned long stime, utime; unsigned long stime, utime;
unsigned long start_time;
unsigned pid; unsigned pid;
unsigned ppid; unsigned ppid;
unsigned pgid; unsigned pgid;
@ -1032,11 +1033,12 @@ enum {
PSSCAN_SMAPS = (1 << 15) * ENABLE_FEATURE_TOPMEM, PSSCAN_SMAPS = (1 << 15) * ENABLE_FEATURE_TOPMEM,
PSSCAN_ARGVN = (1 << 16) * (ENABLE_PGREP | ENABLE_PKILL), PSSCAN_ARGVN = (1 << 16) * (ENABLE_PGREP | ENABLE_PKILL),
USE_SELINUX(PSSCAN_CONTEXT = 1 << 17,) USE_SELINUX(PSSCAN_CONTEXT = 1 << 17,)
PSSCAN_START_TIME = 1 << 18,
/* These are all retrieved from proc/NN/stat in one go: */ /* These are all retrieved from proc/NN/stat in one go: */
PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
| PSSCAN_COMM | PSSCAN_STATE | PSSCAN_COMM | PSSCAN_STATE
| PSSCAN_VSZ | PSSCAN_RSS | PSSCAN_VSZ | PSSCAN_RSS
| PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
| PSSCAN_TTY, | PSSCAN_TTY,
}; };
procps_status_t* alloc_procps_scan(int flags); procps_status_t* alloc_procps_scan(int flags);

View File

@ -243,7 +243,8 @@ procps_status_t *procps_scan(procps_status_t* sp, int flags)
"%lu %lu " /* utime, stime */ "%lu %lu " /* utime, stime */
"%*s %*s %*s " /* cutime, cstime, priority */ "%*s %*s %*s " /* cutime, cstime, priority */
"%ld " /* nice */ "%ld " /* nice */
"%*s %*s %*s " /* timeout, it_real_value, start_time */ "%*s %*s " /* timeout, it_real_value */
"%lu " /* start_time */
"%lu " /* vsize */ "%lu " /* vsize */
"%lu " /* rss */ "%lu " /* rss */
/* "%lu %lu %lu %lu %lu %lu " rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */ /* "%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->pgid, &sp->sid, &tty,
&sp->utime, &sp->stime, &sp->utime, &sp->stime,
&tasknice, &tasknice,
&sp->start_time,
&vsz, &vsz,
&rss); &rss);
if (n != 10) if (n != 10)
@ -280,7 +282,8 @@ procps_status_t *procps_scan(procps_status_t* sp, int flags)
sp->stime = fast_strtoul_10(&cp); sp->stime = fast_strtoul_10(&cp);
cp = skip_fields(cp, 3); /* cutime, cstime, priority */ cp = skip_fields(cp, 3); /* cutime, cstime, priority */
tasknice = fast_strtoul_10(&cp); 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 */ /* vsz is in bytes and we want kb */
sp->vsz = fast_strtoul_10(&cp) >> 10; sp->vsz = fast_strtoul_10(&cp) >> 10;
/* vsz is in bytes but rss is in *PAGES*! Can you believe that? */ /* vsz is in bytes but rss is in *PAGES*! Can you believe that? */

View File

@ -99,6 +99,21 @@ config FEATURE_PS_WIDE
If given once, 132 chars are printed and given more than If given once, 132 chars are printed and given more than
one, the length is unlimited. 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 config RENICE
bool "renice" bool "renice"
default n default n

View File

@ -16,6 +16,143 @@ enum { MAX_WIDTH = 2*1024 };
#if ENABLE_DESKTOP #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') */ /* Print value to buf, max size+1 chars (including trailing '\0') */
static void func_user(char *buf, int size, const procps_status_t *ps) 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); 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 #if ENABLE_SELINUX
static void func_label(char *buf, int size, const procps_status_t *ps) 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->??? 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) 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[] = { static const ps_out_t out_spec[] = {
// Mandated by POSIX: // Mandated by POSIX:
{ 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID }, { 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 , "pid" ,"PID" ,func_pid ,PSSCAN_PID },
{ 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID },
{ 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, { 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("GROUP" )-1, "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID },
// { sizeof("NI" )-1, "nice" ,"NI" ,func_nice ,PSSCAN_ }, // { sizeof("NI" )-1, "nice" ,"NI" ,func_nice ,PSSCAN_ },
// { sizeof("%CPU" )-1, "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ }, // { sizeof("%CPU" )-1, "pcpu" ,"%CPU" ,func_pcpu ,PSSCAN_ },
// { sizeof("RGROUP" )-1, "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID }, // { sizeof("RGROUP" )-1, "rgroup","RGROUP" ,func_rgroup,PSSCAN_UIDGID },
// { sizeof("RUSER" )-1, "ruser" ,"RUSER" ,func_ruser ,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 }, { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY },
{ 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ },
// Not mandated by POSIX, but useful: // Not mandated by POSIX, but useful:
@ -133,31 +284,6 @@ static const ps_out_t out_spec[] = {
#endif #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) static ps_out_t* new_out_t(void)
{ {
int i = out_cnt++; int i = out_cnt++;