replace /proc scanning code by more versatile one.
authorDenis Vlasenko <vda.linux@googlemail.com>
Sun, 5 Nov 2006 00:43:51 +0000 (00:43 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sun, 5 Nov 2006 00:43:51 +0000 (00:43 -0000)
Use it where appropriate.
Stop scanning /etc/passwd *for every process*!!! (uid->username)
top: reduce memory usage - we won't save unneeded fields
from /proc info anymore. Downside: ~+250 bytes of code

include/libbb.h
libbb/find_pid_by_name.c
libbb/procps.c
procps/kill.c
procps/ps.c
procps/top.c

index 7b5221d87df93cf5f120dc25d771553279e55566..60743311876d033097ccd4d5be2015cd203c1947 100644 (file)
@@ -501,23 +501,47 @@ enum { COMM_LEN = 16 };
 #endif
 #endif
 typedef struct {
-       int pid, ppid;
-       char user[9];
-       char state[4];
+       DIR *dir;
+/* Fields are set to 0/NULL if failed to determine (or not requested) */
+       char *cmd;
        unsigned long rss;
-#ifdef CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-       unsigned pcpu;
        unsigned long stime, utime;
-#endif
-       char *cmd;
-
-       /* basename of executable file in call to exec(2),
-               size from kernel headers */
-       char short_cmd[COMM_LEN];
+       unsigned pid;
+       unsigned ppid;
+       unsigned pgid;
+       unsigned sid;
+       unsigned uid;
+       unsigned gid;
+       /* basename of executable file in call to exec(2), size from */
+       /* sizeof(task_struct.comm) in /usr/include/linux/sched.h */
+       char state[4];
+       char comm[COMM_LEN];
+//     user/group? - use passwd/group parsing functions
 } procps_status_t;
-procps_status_t* procps_scan(int save_user_arg0);
+enum {
+       PSSCAN_PID      = 1 << 0,
+       PSSCAN_PPID     = 1 << 1,
+       PSSCAN_PGID     = 1 << 2,
+       PSSCAN_SID      = 1 << 3,
+       PSSCAN_UIDGID   = 1 << 4,
+       PSSCAN_COMM     = 1 << 5,
+       PSSCAN_CMD      = 1 << 6,
+       PSSCAN_STATE    = 1 << 7,
+       PSSCAN_RSS      = 1 << 8,
+       PSSCAN_STIME    = 1 << 9,
+       PSSCAN_UTIME    = 1 << 10,
+       /* These are all retrieved from proc/NN/stat in one go: */
+       PSSCAN_STAT     = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
+                       | PSSCAN_COMM | PSSCAN_STATE
+                       | PSSCAN_RSS | PSSCAN_STIME | PSSCAN_UTIME,
+};
+procps_status_t* alloc_procps_scan(int flags);
+void free_procps_scan(procps_status_t* sp);
+procps_status_t* procps_scan(procps_status_t* sp, int flags);
 pid_t *find_pid_by_name(const char* procName);
 pid_t *pidlist_reverse(pid_t *pidList);
+void clear_username_cache(void);
+const char* get_cached_username(uid_t uid);
 
 
 extern const char bb_uuenc_tbl_base64[];
index 05f7f968f872cfb443166ea9a03bd7d8e3f49123..e98616940c061ce28b9a62d74203ae7ac13fd590 100644 (file)
@@ -23,11 +23,11 @@ pid_t* find_pid_by_name(const char* procName)
 {
        pid_t* pidList;
        int i = 0;
-       procps_status_t* p;
+       procps_status_t* p = NULL;
 
        pidList = xmalloc(sizeof(*pidList));
-       while ((p = procps_scan(0)) != 0) {
-               if (strncmp(p->short_cmd, procName, COMM_LEN-1) == 0) {
+       while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM))) {
+               if (strncmp(p->comm, procName, sizeof(p->comm)-1) == 0) {
                        pidList = xrealloc(pidList, sizeof(*pidList) * (i+2));
                        pidList[i++] = p->pid;
                }
index 15f77153a491e7454ee6999d7550723d1a759dee..dee5638e433bf9306ee4009becf804be837522b1 100644 (file)
 #include "libbb.h"
 
 
+typedef struct {
+       uid_t uid;
+       char username[12];
+} user_map_t;
+
+static user_map_t *username_cache;
+static int username_cache_size;
+
+void clear_username_cache(void)
+{
+       free(username_cache);
+       username_cache = NULL;
+       username_cache_size = 0;
+}
+
+const char* get_cached_username(uid_t uid)
+{
+       int i;
+       for (i = 0; i < username_cache_size; i++)
+               if (username_cache[i].uid == uid)
+                       return username_cache[i].username;
+       i = username_cache_size++;
+       username_cache = xrealloc(username_cache, username_cache_size * sizeof(*username_cache));
+       username_cache[i].uid = uid;
+       bb_getpwuid(username_cache[i].username, uid, sizeof(username_cache[i].username));
+       return username_cache[i].username;
+}
+
+
 #define PROCPS_BUFSIZE 1024
 
 static int read_to_buf(const char *filename, void *buf)
@@ -21,119 +50,171 @@ static int read_to_buf(const char *filename, void *buf)
        return ret;
 }
 
+procps_status_t* alloc_procps_scan(int flags)
+{
+       procps_status_t* sp = xzalloc(sizeof(procps_status_t));
+       sp->dir = xopendir("/proc");
+       return sp;
+}
 
-procps_status_t * procps_scan(int save_user_arg0)
+void free_procps_scan(procps_status_t* sp)
 {
-       static DIR *dir;
-       static procps_status_t ret_status;
+       closedir(sp->dir);
+       free(sp->cmd);
+       free(sp);
+}
 
+void BUG_comm_size(void);
+procps_status_t* procps_scan(procps_status_t* sp, int flags)
+{
        struct dirent *entry;
-       char *name;
        char buf[PROCPS_BUFSIZE];
-       char status[sizeof("/proc//cmdline") + sizeof(int)*3];
-       char *status_tail;
-       procps_status_t curstatus;
+       char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
+       char *filename_tail;
        long tasknice;
-       int pid;
+       unsigned pid;
        int n;
        struct stat sb;
 
-       if (!dir) {
-               dir = xopendir("/proc");
-       }
+       if (!sp)
+               sp = alloc_procps_scan(flags);
+
        for (;;) {
-               entry = readdir(dir);
+               entry = readdir(sp->dir);
                if (entry == NULL) {
-                       closedir(dir);
-                       dir = 0;
-                       return 0;
+                       free_procps_scan(sp);
+                       return NULL;
                }
-               name = entry->d_name;
-               if (!(*name >= '0' && *name <= '9'))
+               if (safe_strtou(entry->d_name, &pid))
                        continue;
 
-               memset(&curstatus, 0, sizeof(procps_status_t));
-               pid = atoi(name);
-               curstatus.pid = pid;
+               /* After this point we have to break, not continue
+                * ("continue" would mean that current /proc/NNN
+                * is not a valid process info) */
 
-               status_tail = status + sprintf(status, "/proc/%d", pid);
-               if (stat(status, &sb))
-                       continue;
-               bb_getpwuid(curstatus.user, sb.st_uid, sizeof(curstatus.user));
+               memset(&sp->rss, 0, sizeof(*sp) - offsetof(procps_status_t, rss));
 
-               /* see proc(5) for some details on this */
-               strcpy(status_tail, "/stat");
-               n = read_to_buf(status, buf);
-               if (n < 0)
-                       continue;
-               name = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
-               if (name == 0 || name[1] != ' ')
-                       continue;
-               *name = 0;
-               sscanf(buf, "%*s (%15c", curstatus.short_cmd);
-               n = sscanf(name+2,
-               "%c %d "
-               "%*s %*s %*s %*s "     /* pgrp, session, tty, tpgid */
-               "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
-#ifdef CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-               "%lu %lu "             /* utime, stime */
-#else
-               "%*s %*s "             /* utime, stime */
-#endif
-               "%*s %*s %*s "         /* cutime, cstime, priority */
-               "%ld "                 /* nice */
-               "%*s %*s %*s "         /* timeout, it_real_value, start_time */
-               "%*s "                 /* vsize */
-               "%ld",                 /* rss */
-               curstatus.state, &curstatus.ppid,
-#ifdef CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-               &curstatus.utime, &curstatus.stime,
-#endif
-               &tasknice,
-               &curstatus.rss);
-#ifdef CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE
-               if (n != 6)
-#else
-               if (n != 4)
-#endif
-                       continue;
+               sp->pid = pid;
+               if (!(flags & ~PSSCAN_PID)) break;
 
-               if (curstatus.rss == 0 && curstatus.state[0] != 'Z')
-                       curstatus.state[1] = 'W';
-               else
-                       curstatus.state[1] = ' ';
-               if (tasknice < 0)
-                       curstatus.state[2] = '<';
-               else if (tasknice > 0)
-                       curstatus.state[2] = 'N';
-               else
-                       curstatus.state[2] = ' ';
+               filename_tail = filename + sprintf(filename, "/proc/%d", pid);
+
+               if (flags & PSSCAN_UIDGID) {
+                       if (stat(filename, &sb))
+                               break;
+                       /* Need comment - is this effective or read UID/GID? */
+                       sp->uid = sb.st_uid;
+                       sp->gid = sb.st_gid;
+               }
+       
+               if (flags & PSSCAN_STAT) {
+                       char *cp;
+                       /* see proc(5) for some details on this */
+                       strcpy(filename_tail, "/stat");
+                       n = read_to_buf(filename, buf);
+                       if (n < 0)
+                               break;
+                       cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
+                       if (!cp || cp[1] != ' ')
+                               break;
+                       cp[0] = '\0';
+                       if (sizeof(sp->comm) < 16)
+                               BUG_comm_size();
+                       sscanf(buf, "%*s (%15c", sp->comm);
+                       n = sscanf(cp+2,
+                               "%c %u "               /* state, ppid */
+                               "%u %u %*s %*s "       /* pgid, sid, tty, tpgid */
+                               "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
+                               "%lu %lu "             /* utime, stime */
+                               "%*s %*s %*s "         /* cutime, cstime, priority */
+                               "%ld "                 /* nice */
+                               "%*s %*s %*s "         /* timeout, it_real_value, start_time */
+                               "%*s "                 /* vsize */
+                               "%lu",                 /* rss */
+                               sp->state, &sp->ppid,
+                               &sp->pgid, &sp->sid,
+                               &sp->utime, &sp->stime,
+                               &tasknice,
+                               &sp->rss);
+                       if (n != 8)
+                               break;
+
+                       if (sp->rss == 0 && sp->state[0] != 'Z')
+                               sp->state[1] = 'W';
+                       else
+                               sp->state[1] = ' ';
+                       if (tasknice < 0)
+                               sp->state[2] = '<';
+                       else if (tasknice > 0)
+                               sp->state[2] = 'N';
+                       else
+                               sp->state[2] = ' ';
 
 #ifdef PAGE_SHIFT
-               curstatus.rss <<= (PAGE_SHIFT - 10);     /* 2**10 = 1kb */
+                       sp->rss <<= (PAGE_SHIFT - 10);     /* 2**10 = 1kb */
 #else
-               curstatus.rss *= (getpagesize() >> 10);     /* 2**10 = 1kb */
+                       sp->rss *= (getpagesize() >> 10);     /* 2**10 = 1kb */
 #endif
+               }
 
-               if (save_user_arg0) {
-                       strcpy(status_tail, "/cmdline");
-                       n = read_to_buf(status, buf);
-                       if (n > 0) {
-                               if (buf[n-1]=='\n')
-                                       buf[--n] = 0;
-                               name = buf;
-                               while (n) {
-                                       if (((unsigned char)*name) < ' ')
-                                               *name = ' ';
-                                       name++;
-                                       n--;
-                               }
-                               *name = 0;
-                               if (buf[0])
-                                       curstatus.cmd = strdup(buf);
-                               /* if NULL it work true also */
+               if (flags & PSSCAN_CMD) {
+                       free(sp->cmd);
+                       sp->cmd = NULL;
+                       strcpy(filename_tail, "/cmdline");
+                       n = read_to_buf(filename, buf);
+                       if (n <= 0)
+                               break;
+                       if (buf[n-1] == '\n') {
+                               if (!--n)
+                                       break;
+                               buf[n] = '\0';
                        }
+                       do {
+                               n--;
+                               if ((unsigned char)(buf[n]) < ' ')
+                                       buf[n] = ' ';
+                       } while (n);
+                       sp->cmd = strdup(buf);
                }
-               return memcpy(&ret_status, &curstatus, sizeof(procps_status_t));
+               break;
        }
+       return sp;
 }
+/* from kernel:
+       //             pid comm S ppid pgid sid tty_nr tty_pgrp flg
+        sprintf(buffer,"%d (%s) %c %d  %d   %d  %d     %d       %lu %lu \
+%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
+%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n",
+                task->pid,
+                tcomm,
+                state,
+                ppid,
+                pgid,
+                sid,
+                tty_nr,
+                tty_pgrp,
+                task->flags,
+                min_flt,
+
+                cmin_flt,
+                maj_flt,
+                cmaj_flt,
+                cputime_to_clock_t(utime),
+                cputime_to_clock_t(stime),
+                cputime_to_clock_t(cutime),
+                cputime_to_clock_t(cstime),
+                priority,
+                nice,
+                num_threads,
+       // 0,
+                start_time,
+                vsize,
+                mm ? get_mm_rss(mm) : 0,
+                rsslim,
+                mm ? mm->start_code : 0,
+                mm ? mm->end_code : 0,
+                mm ? mm->start_stack : 0,
+                esp,
+                eip,
+the rest is some obsolete cruft
+*/
index 9b96b4c1a9aa236bb5a8f2bec6303b580d6070e4..18121f06fbc3f267577793c765c59ed75c3d7f8a 100644 (file)
@@ -82,7 +82,7 @@ do_it_now:
 
        if (killall5) {
                pid_t sid;
-               procps_status_t* p;
+               procps_status_t* p = NULL;
 
 // Cannot happen anyway? We don't TERM ourself, we STOP
 //             /* kill(-1, sig) on Linux (at least 2.1.x)
@@ -94,8 +94,8 @@ do_it_now:
                pid = getpid();
                sid = getsid(pid);
                /* Now kill all processes except our session */
-               while ((p = procps_scan(0))!=0) {
-                       if (getsid(p->pid)!=sid && p->pid!=pid && p->pid!=1)
+               while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) {
+                       if (p->sid != sid && p->pid != pid && p->pid != 1)
                                kill(p->pid, signo);
                }
                /* And let them continue */
index 9d6e42d79b655d3975cc5bc3414fd8f864175c44..2ff6e77d46f97257e67b2c20d51de52900e3250e 100644 (file)
@@ -11,7 +11,7 @@
 
 int ps_main(int argc, char **argv)
 {
-       procps_status_t * p;
+       procps_status_t *p = NULL;
        int i, len;
        SKIP_SELINUX(const) int use_selinux = 0;
        USE_SELINUX(security_context_t sid = NULL;)
@@ -50,7 +50,13 @@ int ps_main(int argc, char **argv)
        else
                puts("  PID  Uid     VmSize Stat Command");
 
-       while ((p = procps_scan(1)) != 0)  {
+       while ((p = procps_scan(p, 0
+                       | PSSCAN_PID
+                       | PSSCAN_UIDGID
+                       | PSSCAN_STATE
+                       | PSSCAN_RSS
+                       | PSSCAN_CMD
+       ))) {
                char *namecmd = p->cmd;
 #if ENABLE_SELINUX
                if (use_selinux) {
@@ -71,13 +77,18 @@ int ps_main(int argc, char **argv)
                        } else {
                                safe_strncpy(sbuf, "unknown", 7);
                        }
-                       len = printf("%5u %-32s %s ", (unsigned)p->pid, sbuf, p->state);
+                       len = printf("%5u %-32s %s ", p->pid, sbuf, p->state);
                } else
 #endif
+               {
+                       const char *user = get_cached_username(p->uid);
                        if (p->rss == 0)
-                               len = printf("%5u %-8s        %s ", (unsigned)p->pid, p->user, p->state);
+                               len = printf("%5u %-8s        %s ",
+                                       p->pid, user, p->state);
                        else
-                               len = printf("%5u %-8s %6ld %s ", (unsigned)p->pid, p->user, p->rss, p->state);
+                               len = printf("%5u %-8s %6ld %s ",
+                                       p->pid, user, p->rss, p->state);
+               }
 
                i = terminal_width-len;
 
@@ -88,16 +99,15 @@ int ps_main(int argc, char **argv)
                                namecmd[i] = 0;
                        puts(namecmd);
                } else {
-                       namecmd = p->short_cmd;
+                       namecmd = p->comm;
                        if (i < 2)
                                i = 2;
                        if (strlen(namecmd) > ((size_t)i-2))
                                namecmd[i-2] = 0;
                        printf("[%s]\n", namecmd);
                }
-               /* no check needed, but to make valgrind happy.. */
-               if (ENABLE_FEATURE_CLEAN_UP && p->cmd)
-                       free(p->cmd);
        }
+       if (ENABLE_FEATURE_CLEAN_UP)
+               clear_username_cache();
        return EXIT_SUCCESS;
 }
index c76fdc3129d9f8561563d6211d1aaa390184f928..8d732d4b28dc29c42ef1eaf337bb87a95ccfb29b 100644 (file)
 
 #include "busybox.h"
 
-typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q);
 
-static procps_status_t *top;   /* Hehe */
+typedef struct {
+       unsigned long rss;
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+       unsigned long ticks;
+       unsigned pcpu; /* delta of ticks */
+#endif
+       unsigned pid, ppid;
+       unsigned uid;
+       char state[4];
+       char comm[COMM_LEN];
+} top_status_t;
+static top_status_t *top;
 static int ntop;
+/* This structure stores some critical information from one frame to
+   the next. Used for finding deltas. */
+struct save_hist {
+       unsigned long ticks;
+       unsigned pid;
+};
+static struct save_hist *prev_hist;
+static int prev_hist_count;
+/* static int hist_iterations; */
+static unsigned total_pcpu;
+/* static unsigned long total_rss; */
+
+
 #define OPT_BATCH_MODE (option_mask32 & 0x4)
 
 #if ENABLE_FEATURE_USE_TERMIOS
-static int pid_sort(procps_status_t *P, procps_status_t *Q)
+static int pid_sort(top_status_t *P, top_status_t *Q)
 {
+       /* Buggy wrt pids with high bit set */
+       /* (linux pids are in [1..2^15-1]) */
        return (Q->pid - P->pid);
 }
 #endif
 
-static int mem_sort(procps_status_t *P, procps_status_t *Q)
+static int mem_sort(top_status_t *P, top_status_t *Q)
 {
-       return (int)(Q->rss - P->rss);
+       /* We want to avoid unsigned->signed and truncation errors */
+       if (Q->rss < P->rss) return -1;
+       return Q->rss != P->rss; /* 0 if ==, 1 if > */
 }
 
-#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+
+typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
+
+#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+
+static cmp_funcp sort_function;
+
+#else
 
 enum { SORT_DEPTH = 3 };
-static cmp_t sort_function[SORT_DEPTH];
 
-static int pcpu_sort(procps_status_t *P, procps_status_t *Q)
+static cmp_funcp sort_function[SORT_DEPTH];
+
+static int pcpu_sort(top_status_t *P, top_status_t *Q)
 {
-       return (Q->pcpu - P->pcpu);
+       /* Buggy wrt ticks with high bit set */
+       /* Affects only processes for which ticks overflow */
+       return (int)Q->pcpu - (int)P->pcpu;
 }
 
-static int time_sort(procps_status_t *P, procps_status_t *Q)
+static int time_sort(top_status_t *P, top_status_t *Q)
 {
-       return (int)((Q->stime + Q->utime) - (P->stime + P->utime));
+       /* We want to avoid unsigned->signed and truncation errors */
+       if (Q->ticks < P->ticks) return -1;
+       return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
 }
 
 static int mult_lvl_cmp(void* a, void* b) {
@@ -74,24 +113,6 @@ static int mult_lvl_cmp(void* a, void* b) {
        return 0;
 }
 
-/* This structure stores some critical information from one frame to
-   the next. Mostly used for sorting. */
-struct save_hist {
-       int ticks;
-       pid_t pid;
-};
-
-/*
- * Calculates percent cpu usage for each task.
- */
-
-static struct save_hist *prev_hist;
-static int prev_hist_count;
-/* static int hist_iterations; */
-
-
-static unsigned total_pcpu;
-/* static unsigned long total_rss; */
 
 typedef struct {
        unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal;
@@ -115,11 +136,12 @@ static void get_jiffy_counts(void)
        jif.busy = jif.total - jif.idle - jif.iowait;
 }
 
+
 static void do_stats(void)
 {
-       procps_status_t *cur;
+       top_status_t *cur;
        pid_t pid;
-       int total_time, i, last_i, n;
+       int i, last_i, n;
        struct save_hist *new_hist;
 
        get_jiffy_counts();
@@ -139,8 +161,7 @@ static void do_stats(void)
                 * and system time
                 */
                pid = cur->pid;
-               total_time = cur->stime + cur->utime;
-               new_hist[n].ticks = total_time;
+               new_hist[n].ticks = cur->ticks;
                new_hist[n].pid = pid;
 
                /* find matching entry from previous pass */
@@ -150,13 +171,13 @@ static void do_stats(void)
                last_i = i;
                if (prev_hist_count) do {
                        if (prev_hist[i].pid == pid) {
-                               cur->pcpu = total_time - prev_hist[i].ticks;
+                               cur->pcpu = cur->ticks - prev_hist[i].ticks;
+                               total_pcpu += cur->pcpu;
                                break;
                        }
                        i = (i+1) % prev_hist_count;
                        /* hist_iterations++; */
                } while (i != last_i);
-               total_pcpu += cur->pcpu;
                /* total_rss += cur->rss; */
        }
 
@@ -167,10 +188,9 @@ static void do_stats(void)
        prev_hist = new_hist;
        prev_hist_count = ntop;
 }
-#else
-static cmp_t sort_function;
 #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
 
+
 /* display generic info (meminfo / loadavg) */
 static unsigned long display_generic(int scr_width)
 {
@@ -265,7 +285,7 @@ static void display_status(int count, int scr_width)
                bits_per_int = sizeof(int)*8
        };
 
-       procps_status_t *s = top;
+       top_status_t *s = top;
        char rss_str_buf[8];
        unsigned long total_memory = display_generic(scr_width); /* or use total_rss? */
        unsigned pmem_shift, pmem_scale;
@@ -333,14 +353,19 @@ static void display_status(int count, int scr_width)
                        sprintf(rss_str_buf, "%6ldM", s->rss/1024);
                else
                        sprintf(rss_str_buf, "%7ld", s->rss);
-               USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(pcpu = div((s->pcpu*pcpu_scale) >> pcpu_shift, 10);)
-               col -= printf("\n%5u %-8s %s  %s%6u%3u.%c" \
-                               USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE("%3u.%c") " ",
-                               (unsigned)s->pid, s->user, s->state, rss_str_buf, (unsigned)s->ppid,
+               USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(
+               pcpu = div((s->pcpu*pcpu_scale) >> pcpu_shift, 10);
+               )
+               col -= printf("\n%5u %-8s %s  "
+                               "%s%6u"
+                               USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE("%3u.%c")
+                               "%3u.%c ",
+                               s->pid, get_cached_username(s->uid), s->state,
+                               rss_str_buf, s->ppid,
                                USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(pcpu.quot, '0'+pcpu.rem,)
                                pmem.quot, '0'+pmem.rem);
                if (col > 0)
-                       printf("%.*s", col, s->short_cmd);
+                       printf("%.*s", col, s->comm);
                /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
                        jif.busy - prev_jif.busy, jif.total - prev_jif.total); */
                s++;
@@ -350,18 +375,20 @@ static void display_status(int count, int scr_width)
        fflush(stdout);
 }
 
+
 static void clearmems(void)
 {
+       clear_username_cache();
        free(top);
        top = 0;
        ntop = 0;
 }
 
+
 #if ENABLE_FEATURE_USE_TERMIOS
 #include <termios.h>
 #include <signal.h>
 
-
 static struct termios initial_settings;
 
 static void reset_term(void)
@@ -427,7 +454,7 @@ int top_main(int argc, char **argv)
 #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
 
        while (1) {
-               procps_status_t *p;
+               procps_status_t *p = NULL;
 
                /* Default to 25 lines - 5 lines for status */
                lines = 24 - 3;
@@ -442,11 +469,26 @@ int top_main(int argc, char **argv)
 #endif /* FEATURE_USE_TERMIOS */
 
                /* read process IDs & status for all the processes */
-               while ((p = procps_scan(0)) != 0) {
+               while ((p = procps_scan(p, 0
+                               | PSSCAN_PID
+                               | PSSCAN_PPID
+                               | PSSCAN_RSS
+                               | PSSCAN_STIME
+                               | PSSCAN_UTIME
+                               | PSSCAN_STATE
+                               | PSSCAN_COMM
+                               | PSSCAN_SID
+                               | PSSCAN_UIDGID
+               ))) {
                        int n = ntop;
-
-                       top = xrealloc(top, (++ntop)*sizeof(procps_status_t));
-                       memcpy(top + n, p, sizeof(procps_status_t));
+                       top = xrealloc(top, (++ntop)*sizeof(top_status_t));
+                       top[n].pid = p->pid;
+                       top[n].ppid = p->ppid;
+                       top[n].rss = p->rss;
+                       top[n].ticks = p->stime + p->utime;
+                       top[n].uid = p->uid;
+                       strcpy(top[n].state, p->state);
+                       strcpy(top[n].comm, p->comm);
                }
                if (ntop == 0) {
                        bb_error_msg_and_die("can't find process info in /proc");
@@ -459,9 +501,9 @@ int top_main(int argc, char **argv)
                        continue;
                }
                do_stats();
-               qsort(top, ntop, sizeof(procps_status_t), (void*)mult_lvl_cmp);
+               qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
 #else
-               qsort(top, ntop, sizeof(procps_status_t), (void*)sort_function);
+               qsort(top, ntop, sizeof(top_status_t), (void*)sort_function);
 #endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
                count = lines;
                if (OPT_BATCH_MODE || count > ntop) {