aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2006-11-05 00:43:51 +0000
committervda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2006-11-05 00:43:51 +0000
commit48b1ebd12ec4ef182b4ea423f2309705ae141e8e (patch)
tree5deb7d2b82c280440761cceb4281738867d5631a
parentd2c185589694b83e492144a6a5d0f79e16dd9b74 (diff)
downloadbusybox-w32-48b1ebd12ec4ef182b4ea423f2309705ae141e8e.tar.gz
busybox-w32-48b1ebd12ec4ef182b4ea423f2309705ae141e8e.tar.bz2
busybox-w32-48b1ebd12ec4ef182b4ea423f2309705ae141e8e.zip
replace /proc scanning code by more versatile one.
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 git-svn-id: svn://busybox.net/trunk/busybox@16509 69ca8d6d-28ef-0310-b511-8ec308f3f277
-rw-r--r--include/libbb.h48
-rw-r--r--libbb/find_pid_by_name.c6
-rw-r--r--libbb/procps.c261
-rw-r--r--procps/kill.c6
-rw-r--r--procps/ps.c28
-rw-r--r--procps/top.c144
6 files changed, 325 insertions, 168 deletions
diff --git a/include/libbb.h b/include/libbb.h
index 7b5221d87..607433118 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -501,23 +501,47 @@ enum { COMM_LEN = 16 };
501#endif 501#endif
502#endif 502#endif
503typedef struct { 503typedef struct {
504 int pid, ppid; 504 DIR *dir;
505 char user[9]; 505/* Fields are set to 0/NULL if failed to determine (or not requested) */
506 char state[4]; 506 char *cmd;
507 unsigned long rss; 507 unsigned long rss;
508#ifdef CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE
509 unsigned pcpu;
510 unsigned long stime, utime; 508 unsigned long stime, utime;
511#endif 509 unsigned pid;
512 char *cmd; 510 unsigned ppid;
513 511 unsigned pgid;
514 /* basename of executable file in call to exec(2), 512 unsigned sid;
515 size from kernel headers */ 513 unsigned uid;
516 char short_cmd[COMM_LEN]; 514 unsigned gid;
515 /* basename of executable file in call to exec(2), size from */
516 /* sizeof(task_struct.comm) in /usr/include/linux/sched.h */
517 char state[4];
518 char comm[COMM_LEN];
519// user/group? - use passwd/group parsing functions
517} procps_status_t; 520} procps_status_t;
518procps_status_t* procps_scan(int save_user_arg0); 521enum {
522 PSSCAN_PID = 1 << 0,
523 PSSCAN_PPID = 1 << 1,
524 PSSCAN_PGID = 1 << 2,
525 PSSCAN_SID = 1 << 3,
526 PSSCAN_UIDGID = 1 << 4,
527 PSSCAN_COMM = 1 << 5,
528 PSSCAN_CMD = 1 << 6,
529 PSSCAN_STATE = 1 << 7,
530 PSSCAN_RSS = 1 << 8,
531 PSSCAN_STIME = 1 << 9,
532 PSSCAN_UTIME = 1 << 10,
533 /* These are all retrieved from proc/NN/stat in one go: */
534 PSSCAN_STAT = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
535 | PSSCAN_COMM | PSSCAN_STATE
536 | PSSCAN_RSS | PSSCAN_STIME | PSSCAN_UTIME,
537};
538procps_status_t* alloc_procps_scan(int flags);
539void free_procps_scan(procps_status_t* sp);
540procps_status_t* procps_scan(procps_status_t* sp, int flags);
519pid_t *find_pid_by_name(const char* procName); 541pid_t *find_pid_by_name(const char* procName);
520pid_t *pidlist_reverse(pid_t *pidList); 542pid_t *pidlist_reverse(pid_t *pidList);
543void clear_username_cache(void);
544const char* get_cached_username(uid_t uid);
521 545
522 546
523extern const char bb_uuenc_tbl_base64[]; 547extern const char bb_uuenc_tbl_base64[];
diff --git a/libbb/find_pid_by_name.c b/libbb/find_pid_by_name.c
index 05f7f968f..e98616940 100644
--- a/libbb/find_pid_by_name.c
+++ b/libbb/find_pid_by_name.c
@@ -23,11 +23,11 @@ pid_t* find_pid_by_name(const char* procName)
23{ 23{
24 pid_t* pidList; 24 pid_t* pidList;
25 int i = 0; 25 int i = 0;
26 procps_status_t* p; 26 procps_status_t* p = NULL;
27 27
28 pidList = xmalloc(sizeof(*pidList)); 28 pidList = xmalloc(sizeof(*pidList));
29 while ((p = procps_scan(0)) != 0) { 29 while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM))) {
30 if (strncmp(p->short_cmd, procName, COMM_LEN-1) == 0) { 30 if (strncmp(p->comm, procName, sizeof(p->comm)-1) == 0) {
31 pidList = xrealloc(pidList, sizeof(*pidList) * (i+2)); 31 pidList = xrealloc(pidList, sizeof(*pidList) * (i+2));
32 pidList[i++] = p->pid; 32 pidList[i++] = p->pid;
33 } 33 }
diff --git a/libbb/procps.c b/libbb/procps.c
index 15f77153a..dee5638e4 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -11,6 +11,35 @@
11#include "libbb.h" 11#include "libbb.h"
12 12
13 13
14typedef struct {
15 uid_t uid;
16 char username[12];
17} user_map_t;
18
19static user_map_t *username_cache;
20static int username_cache_size;
21
22void clear_username_cache(void)
23{
24 free(username_cache);
25 username_cache = NULL;
26 username_cache_size = 0;
27}
28
29const char* get_cached_username(uid_t uid)
30{
31 int i;
32 for (i = 0; i < username_cache_size; i++)
33 if (username_cache[i].uid == uid)
34 return username_cache[i].username;
35 i = username_cache_size++;
36 username_cache = xrealloc(username_cache, username_cache_size * sizeof(*username_cache));
37 username_cache[i].uid = uid;
38 bb_getpwuid(username_cache[i].username, uid, sizeof(username_cache[i].username));
39 return username_cache[i].username;
40}
41
42
14#define PROCPS_BUFSIZE 1024 43#define PROCPS_BUFSIZE 1024
15 44
16static int read_to_buf(const char *filename, void *buf) 45static int read_to_buf(const char *filename, void *buf)
@@ -21,119 +50,171 @@ static int read_to_buf(const char *filename, void *buf)
21 return ret; 50 return ret;
22} 51}
23 52
53procps_status_t* alloc_procps_scan(int flags)
54{
55 procps_status_t* sp = xzalloc(sizeof(procps_status_t));
56 sp->dir = xopendir("/proc");
57 return sp;
58}
24 59
25procps_status_t * procps_scan(int save_user_arg0) 60void free_procps_scan(procps_status_t* sp)
26{ 61{
27 static DIR *dir; 62 closedir(sp->dir);
28 static procps_status_t ret_status; 63 free(sp->cmd);
64 free(sp);
65}
29 66
67void BUG_comm_size(void);
68procps_status_t* procps_scan(procps_status_t* sp, int flags)
69{
30 struct dirent *entry; 70 struct dirent *entry;
31 char *name;
32 char buf[PROCPS_BUFSIZE]; 71 char buf[PROCPS_BUFSIZE];
33 char status[sizeof("/proc//cmdline") + sizeof(int)*3]; 72 char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
34 char *status_tail; 73 char *filename_tail;
35 procps_status_t curstatus;
36 long tasknice; 74 long tasknice;
37 int pid; 75 unsigned pid;
38 int n; 76 int n;
39 struct stat sb; 77 struct stat sb;
40 78
41 if (!dir) { 79 if (!sp)
42 dir = xopendir("/proc"); 80 sp = alloc_procps_scan(flags);
43 } 81
44 for (;;) { 82 for (;;) {
45 entry = readdir(dir); 83 entry = readdir(sp->dir);
46 if (entry == NULL) { 84 if (entry == NULL) {
47 closedir(dir); 85 free_procps_scan(sp);
48 dir = 0; 86 return NULL;
49 return 0;
50 } 87 }
51 name = entry->d_name; 88 if (safe_strtou(entry->d_name, &pid))
52 if (!(*name >= '0' && *name <= '9'))
53 continue; 89 continue;
54 90
55 memset(&curstatus, 0, sizeof(procps_status_t)); 91 /* After this point we have to break, not continue
56 pid = atoi(name); 92 * ("continue" would mean that current /proc/NNN
57 curstatus.pid = pid; 93 * is not a valid process info) */
58 94
59 status_tail = status + sprintf(status, "/proc/%d", pid); 95 memset(&sp->rss, 0, sizeof(*sp) - offsetof(procps_status_t, rss));
60 if (stat(status, &sb))
61 continue;
62 bb_getpwuid(curstatus.user, sb.st_uid, sizeof(curstatus.user));
63 96
64 /* see proc(5) for some details on this */ 97 sp->pid = pid;
65 strcpy(status_tail, "/stat"); 98 if (!(flags & ~PSSCAN_PID)) break;
66 n = read_to_buf(status, buf);
67 if (n < 0)
68 continue;
69 name = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
70 if (name == 0 || name[1] != ' ')
71 continue;
72 *name = 0;
73 sscanf(buf, "%*s (%15c", curstatus.short_cmd);
74 n = sscanf(name+2,
75 "%c %d "
76 "%*s %*s %*s %*s " /* pgrp, session, tty, tpgid */
77 "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
78#ifdef CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE
79 "%lu %lu " /* utime, stime */
80#else
81 "%*s %*s " /* utime, stime */
82#endif
83 "%*s %*s %*s " /* cutime, cstime, priority */
84 "%ld " /* nice */
85 "%*s %*s %*s " /* timeout, it_real_value, start_time */
86 "%*s " /* vsize */
87 "%ld", /* rss */
88 curstatus.state, &curstatus.ppid,
89#ifdef CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE
90 &curstatus.utime, &curstatus.stime,
91#endif
92 &tasknice,
93 &curstatus.rss);
94#ifdef CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE
95 if (n != 6)
96#else
97 if (n != 4)
98#endif
99 continue;
100 99
101 if (curstatus.rss == 0 && curstatus.state[0] != 'Z') 100 filename_tail = filename + sprintf(filename, "/proc/%d", pid);
102 curstatus.state[1] = 'W'; 101
103 else 102 if (flags & PSSCAN_UIDGID) {
104 curstatus.state[1] = ' '; 103 if (stat(filename, &sb))
105 if (tasknice < 0) 104 break;
106 curstatus.state[2] = '<'; 105 /* Need comment - is this effective or read UID/GID? */
107 else if (tasknice > 0) 106 sp->uid = sb.st_uid;
108 curstatus.state[2] = 'N'; 107 sp->gid = sb.st_gid;
109 else 108 }
110 curstatus.state[2] = ' '; 109
110 if (flags & PSSCAN_STAT) {
111 char *cp;
112 /* see proc(5) for some details on this */
113 strcpy(filename_tail, "/stat");
114 n = read_to_buf(filename, buf);
115 if (n < 0)
116 break;
117 cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
118 if (!cp || cp[1] != ' ')
119 break;
120 cp[0] = '\0';
121 if (sizeof(sp->comm) < 16)
122 BUG_comm_size();
123 sscanf(buf, "%*s (%15c", sp->comm);
124 n = sscanf(cp+2,
125 "%c %u " /* state, ppid */
126 "%u %u %*s %*s " /* pgid, sid, tty, tpgid */
127 "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
128 "%lu %lu " /* utime, stime */
129 "%*s %*s %*s " /* cutime, cstime, priority */
130 "%ld " /* nice */
131 "%*s %*s %*s " /* timeout, it_real_value, start_time */
132 "%*s " /* vsize */
133 "%lu", /* rss */
134 sp->state, &sp->ppid,
135 &sp->pgid, &sp->sid,
136 &sp->utime, &sp->stime,
137 &tasknice,
138 &sp->rss);
139 if (n != 8)
140 break;
141
142 if (sp->rss == 0 && sp->state[0] != 'Z')
143 sp->state[1] = 'W';
144 else
145 sp->state[1] = ' ';
146 if (tasknice < 0)
147 sp->state[2] = '<';
148 else if (tasknice > 0)
149 sp->state[2] = 'N';
150 else
151 sp->state[2] = ' ';
111 152
112#ifdef PAGE_SHIFT 153#ifdef PAGE_SHIFT
113 curstatus.rss <<= (PAGE_SHIFT - 10); /* 2**10 = 1kb */ 154 sp->rss <<= (PAGE_SHIFT - 10); /* 2**10 = 1kb */
114#else 155#else
115 curstatus.rss *= (getpagesize() >> 10); /* 2**10 = 1kb */ 156 sp->rss *= (getpagesize() >> 10); /* 2**10 = 1kb */
116#endif 157#endif
158 }
117 159
118 if (save_user_arg0) { 160 if (flags & PSSCAN_CMD) {
119 strcpy(status_tail, "/cmdline"); 161 free(sp->cmd);
120 n = read_to_buf(status, buf); 162 sp->cmd = NULL;
121 if (n > 0) { 163 strcpy(filename_tail, "/cmdline");
122 if (buf[n-1]=='\n') 164 n = read_to_buf(filename, buf);
123 buf[--n] = 0; 165 if (n <= 0)
124 name = buf; 166 break;
125 while (n) { 167 if (buf[n-1] == '\n') {
126 if (((unsigned char)*name) < ' ') 168 if (!--n)
127 *name = ' '; 169 break;
128 name++; 170 buf[n] = '\0';
129 n--;
130 }
131 *name = 0;
132 if (buf[0])
133 curstatus.cmd = strdup(buf);
134 /* if NULL it work true also */
135 } 171 }
172 do {
173 n--;
174 if ((unsigned char)(buf[n]) < ' ')
175 buf[n] = ' ';
176 } while (n);
177 sp->cmd = strdup(buf);
136 } 178 }
137 return memcpy(&ret_status, &curstatus, sizeof(procps_status_t)); 179 break;
138 } 180 }
181 return sp;
139} 182}
183/* from kernel:
184 // pid comm S ppid pgid sid tty_nr tty_pgrp flg
185 sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \
186%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
187%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n",
188 task->pid,
189 tcomm,
190 state,
191 ppid,
192 pgid,
193 sid,
194 tty_nr,
195 tty_pgrp,
196 task->flags,
197 min_flt,
198
199 cmin_flt,
200 maj_flt,
201 cmaj_flt,
202 cputime_to_clock_t(utime),
203 cputime_to_clock_t(stime),
204 cputime_to_clock_t(cutime),
205 cputime_to_clock_t(cstime),
206 priority,
207 nice,
208 num_threads,
209 // 0,
210 start_time,
211 vsize,
212 mm ? get_mm_rss(mm) : 0,
213 rsslim,
214 mm ? mm->start_code : 0,
215 mm ? mm->end_code : 0,
216 mm ? mm->start_stack : 0,
217 esp,
218 eip,
219the rest is some obsolete cruft
220*/
diff --git a/procps/kill.c b/procps/kill.c
index 9b96b4c1a..18121f06f 100644
--- a/procps/kill.c
+++ b/procps/kill.c
@@ -82,7 +82,7 @@ do_it_now:
82 82
83 if (killall5) { 83 if (killall5) {
84 pid_t sid; 84 pid_t sid;
85 procps_status_t* p; 85 procps_status_t* p = NULL;
86 86
87// Cannot happen anyway? We don't TERM ourself, we STOP 87// Cannot happen anyway? We don't TERM ourself, we STOP
88// /* kill(-1, sig) on Linux (at least 2.1.x) 88// /* kill(-1, sig) on Linux (at least 2.1.x)
@@ -94,8 +94,8 @@ do_it_now:
94 pid = getpid(); 94 pid = getpid();
95 sid = getsid(pid); 95 sid = getsid(pid);
96 /* Now kill all processes except our session */ 96 /* Now kill all processes except our session */
97 while ((p = procps_scan(0))!=0) { 97 while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) {
98 if (getsid(p->pid)!=sid && p->pid!=pid && p->pid!=1) 98 if (p->sid != sid && p->pid != pid && p->pid != 1)
99 kill(p->pid, signo); 99 kill(p->pid, signo);
100 } 100 }
101 /* And let them continue */ 101 /* And let them continue */
diff --git a/procps/ps.c b/procps/ps.c
index 9d6e42d79..2ff6e77d4 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -11,7 +11,7 @@
11 11
12int ps_main(int argc, char **argv) 12int ps_main(int argc, char **argv)
13{ 13{
14 procps_status_t * p; 14 procps_status_t *p = NULL;
15 int i, len; 15 int i, len;
16 SKIP_SELINUX(const) int use_selinux = 0; 16 SKIP_SELINUX(const) int use_selinux = 0;
17 USE_SELINUX(security_context_t sid = NULL;) 17 USE_SELINUX(security_context_t sid = NULL;)
@@ -50,7 +50,13 @@ int ps_main(int argc, char **argv)
50 else 50 else
51 puts(" PID Uid VmSize Stat Command"); 51 puts(" PID Uid VmSize Stat Command");
52 52
53 while ((p = procps_scan(1)) != 0) { 53 while ((p = procps_scan(p, 0
54 | PSSCAN_PID
55 | PSSCAN_UIDGID
56 | PSSCAN_STATE
57 | PSSCAN_RSS
58 | PSSCAN_CMD
59 ))) {
54 char *namecmd = p->cmd; 60 char *namecmd = p->cmd;
55#if ENABLE_SELINUX 61#if ENABLE_SELINUX
56 if (use_selinux) { 62 if (use_selinux) {
@@ -71,13 +77,18 @@ int ps_main(int argc, char **argv)
71 } else { 77 } else {
72 safe_strncpy(sbuf, "unknown", 7); 78 safe_strncpy(sbuf, "unknown", 7);
73 } 79 }
74 len = printf("%5u %-32s %s ", (unsigned)p->pid, sbuf, p->state); 80 len = printf("%5u %-32s %s ", p->pid, sbuf, p->state);
75 } else 81 } else
76#endif 82#endif
83 {
84 const char *user = get_cached_username(p->uid);
77 if (p->rss == 0) 85 if (p->rss == 0)
78 len = printf("%5u %-8s %s ", (unsigned)p->pid, p->user, p->state); 86 len = printf("%5u %-8s %s ",
87 p->pid, user, p->state);
79 else 88 else
80 len = printf("%5u %-8s %6ld %s ", (unsigned)p->pid, p->user, p->rss, p->state); 89 len = printf("%5u %-8s %6ld %s ",
90 p->pid, user, p->rss, p->state);
91 }
81 92
82 i = terminal_width-len; 93 i = terminal_width-len;
83 94
@@ -88,16 +99,15 @@ int ps_main(int argc, char **argv)
88 namecmd[i] = 0; 99 namecmd[i] = 0;
89 puts(namecmd); 100 puts(namecmd);
90 } else { 101 } else {
91 namecmd = p->short_cmd; 102 namecmd = p->comm;
92 if (i < 2) 103 if (i < 2)
93 i = 2; 104 i = 2;
94 if (strlen(namecmd) > ((size_t)i-2)) 105 if (strlen(namecmd) > ((size_t)i-2))
95 namecmd[i-2] = 0; 106 namecmd[i-2] = 0;
96 printf("[%s]\n", namecmd); 107 printf("[%s]\n", namecmd);
97 } 108 }
98 /* no check needed, but to make valgrind happy.. */
99 if (ENABLE_FEATURE_CLEAN_UP && p->cmd)
100 free(p->cmd);
101 } 109 }
110 if (ENABLE_FEATURE_CLEAN_UP)
111 clear_username_cache();
102 return EXIT_SUCCESS; 112 return EXIT_SUCCESS;
103} 113}
diff --git a/procps/top.c b/procps/top.c
index c76fdc312..8d732d4b2 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -30,37 +30,76 @@
30 30
31#include "busybox.h" 31#include "busybox.h"
32 32
33typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q);
34 33
35static procps_status_t *top; /* Hehe */ 34typedef struct {
35 unsigned long rss;
36#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
37 unsigned long ticks;
38 unsigned pcpu; /* delta of ticks */
39#endif
40 unsigned pid, ppid;
41 unsigned uid;
42 char state[4];
43 char comm[COMM_LEN];
44} top_status_t;
45static top_status_t *top;
36static int ntop; 46static int ntop;
47/* This structure stores some critical information from one frame to
48 the next. Used for finding deltas. */
49struct save_hist {
50 unsigned long ticks;
51 unsigned pid;
52};
53static struct save_hist *prev_hist;
54static int prev_hist_count;
55/* static int hist_iterations; */
56static unsigned total_pcpu;
57/* static unsigned long total_rss; */
58
59
37#define OPT_BATCH_MODE (option_mask32 & 0x4) 60#define OPT_BATCH_MODE (option_mask32 & 0x4)
38 61
39#if ENABLE_FEATURE_USE_TERMIOS 62#if ENABLE_FEATURE_USE_TERMIOS
40static int pid_sort(procps_status_t *P, procps_status_t *Q) 63static int pid_sort(top_status_t *P, top_status_t *Q)
41{ 64{
65 /* Buggy wrt pids with high bit set */
66 /* (linux pids are in [1..2^15-1]) */
42 return (Q->pid - P->pid); 67 return (Q->pid - P->pid);
43} 68}
44#endif 69#endif
45 70
46static int mem_sort(procps_status_t *P, procps_status_t *Q) 71static int mem_sort(top_status_t *P, top_status_t *Q)
47{ 72{
48 return (int)(Q->rss - P->rss); 73 /* We want to avoid unsigned->signed and truncation errors */
74 if (Q->rss < P->rss) return -1;
75 return Q->rss != P->rss; /* 0 if ==, 1 if > */
49} 76}
50 77
51#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE 78
79typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
80
81#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
82
83static cmp_funcp sort_function;
84
85#else
52 86
53enum { SORT_DEPTH = 3 }; 87enum { SORT_DEPTH = 3 };
54static cmp_t sort_function[SORT_DEPTH];
55 88
56static int pcpu_sort(procps_status_t *P, procps_status_t *Q) 89static cmp_funcp sort_function[SORT_DEPTH];
90
91static int pcpu_sort(top_status_t *P, top_status_t *Q)
57{ 92{
58 return (Q->pcpu - P->pcpu); 93 /* Buggy wrt ticks with high bit set */
94 /* Affects only processes for which ticks overflow */
95 return (int)Q->pcpu - (int)P->pcpu;
59} 96}
60 97
61static int time_sort(procps_status_t *P, procps_status_t *Q) 98static int time_sort(top_status_t *P, top_status_t *Q)
62{ 99{
63 return (int)((Q->stime + Q->utime) - (P->stime + P->utime)); 100 /* We want to avoid unsigned->signed and truncation errors */
101 if (Q->ticks < P->ticks) return -1;
102 return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
64} 103}
65 104
66static int mult_lvl_cmp(void* a, void* b) { 105static int mult_lvl_cmp(void* a, void* b) {
@@ -74,24 +113,6 @@ static int mult_lvl_cmp(void* a, void* b) {
74 return 0; 113 return 0;
75} 114}
76 115
77/* This structure stores some critical information from one frame to
78 the next. Mostly used for sorting. */
79struct save_hist {
80 int ticks;
81 pid_t pid;
82};
83
84/*
85 * Calculates percent cpu usage for each task.
86 */
87
88static struct save_hist *prev_hist;
89static int prev_hist_count;
90/* static int hist_iterations; */
91
92
93static unsigned total_pcpu;
94/* static unsigned long total_rss; */
95 116
96typedef struct { 117typedef struct {
97 unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal; 118 unsigned long long usr,nic,sys,idle,iowait,irq,softirq,steal;
@@ -115,11 +136,12 @@ static void get_jiffy_counts(void)
115 jif.busy = jif.total - jif.idle - jif.iowait; 136 jif.busy = jif.total - jif.idle - jif.iowait;
116} 137}
117 138
139
118static void do_stats(void) 140static void do_stats(void)
119{ 141{
120 procps_status_t *cur; 142 top_status_t *cur;
121 pid_t pid; 143 pid_t pid;
122 int total_time, i, last_i, n; 144 int i, last_i, n;
123 struct save_hist *new_hist; 145 struct save_hist *new_hist;
124 146
125 get_jiffy_counts(); 147 get_jiffy_counts();
@@ -139,8 +161,7 @@ static void do_stats(void)
139 * and system time 161 * and system time
140 */ 162 */
141 pid = cur->pid; 163 pid = cur->pid;
142 total_time = cur->stime + cur->utime; 164 new_hist[n].ticks = cur->ticks;
143 new_hist[n].ticks = total_time;
144 new_hist[n].pid = pid; 165 new_hist[n].pid = pid;
145 166
146 /* find matching entry from previous pass */ 167 /* find matching entry from previous pass */
@@ -150,13 +171,13 @@ static void do_stats(void)
150 last_i = i; 171 last_i = i;
151 if (prev_hist_count) do { 172 if (prev_hist_count) do {
152 if (prev_hist[i].pid == pid) { 173 if (prev_hist[i].pid == pid) {
153 cur->pcpu = total_time - prev_hist[i].ticks; 174 cur->pcpu = cur->ticks - prev_hist[i].ticks;
175 total_pcpu += cur->pcpu;
154 break; 176 break;
155 } 177 }
156 i = (i+1) % prev_hist_count; 178 i = (i+1) % prev_hist_count;
157 /* hist_iterations++; */ 179 /* hist_iterations++; */
158 } while (i != last_i); 180 } while (i != last_i);
159 total_pcpu += cur->pcpu;
160 /* total_rss += cur->rss; */ 181 /* total_rss += cur->rss; */
161 } 182 }
162 183
@@ -167,10 +188,9 @@ static void do_stats(void)
167 prev_hist = new_hist; 188 prev_hist = new_hist;
168 prev_hist_count = ntop; 189 prev_hist_count = ntop;
169} 190}
170#else
171static cmp_t sort_function;
172#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 191#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
173 192
193
174/* display generic info (meminfo / loadavg) */ 194/* display generic info (meminfo / loadavg) */
175static unsigned long display_generic(int scr_width) 195static unsigned long display_generic(int scr_width)
176{ 196{
@@ -265,7 +285,7 @@ static void display_status(int count, int scr_width)
265 bits_per_int = sizeof(int)*8 285 bits_per_int = sizeof(int)*8
266 }; 286 };
267 287
268 procps_status_t *s = top; 288 top_status_t *s = top;
269 char rss_str_buf[8]; 289 char rss_str_buf[8];
270 unsigned long total_memory = display_generic(scr_width); /* or use total_rss? */ 290 unsigned long total_memory = display_generic(scr_width); /* or use total_rss? */
271 unsigned pmem_shift, pmem_scale; 291 unsigned pmem_shift, pmem_scale;
@@ -333,14 +353,19 @@ static void display_status(int count, int scr_width)
333 sprintf(rss_str_buf, "%6ldM", s->rss/1024); 353 sprintf(rss_str_buf, "%6ldM", s->rss/1024);
334 else 354 else
335 sprintf(rss_str_buf, "%7ld", s->rss); 355 sprintf(rss_str_buf, "%7ld", s->rss);
336 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(pcpu = div((s->pcpu*pcpu_scale) >> pcpu_shift, 10);) 356 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(
337 col -= printf("\n%5u %-8s %s %s%6u%3u.%c" \ 357 pcpu = div((s->pcpu*pcpu_scale) >> pcpu_shift, 10);
338 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE("%3u.%c") " ", 358 )
339 (unsigned)s->pid, s->user, s->state, rss_str_buf, (unsigned)s->ppid, 359 col -= printf("\n%5u %-8s %s "
360 "%s%6u"
361 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE("%3u.%c")
362 "%3u.%c ",
363 s->pid, get_cached_username(s->uid), s->state,
364 rss_str_buf, s->ppid,
340 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(pcpu.quot, '0'+pcpu.rem,) 365 USE_FEATURE_TOP_CPU_USAGE_PERCENTAGE(pcpu.quot, '0'+pcpu.rem,)
341 pmem.quot, '0'+pmem.rem); 366 pmem.quot, '0'+pmem.rem);
342 if (col > 0) 367 if (col > 0)
343 printf("%.*s", col, s->short_cmd); 368 printf("%.*s", col, s->comm);
344 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu, 369 /* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
345 jif.busy - prev_jif.busy, jif.total - prev_jif.total); */ 370 jif.busy - prev_jif.busy, jif.total - prev_jif.total); */
346 s++; 371 s++;
@@ -350,18 +375,20 @@ static void display_status(int count, int scr_width)
350 fflush(stdout); 375 fflush(stdout);
351} 376}
352 377
378
353static void clearmems(void) 379static void clearmems(void)
354{ 380{
381 clear_username_cache();
355 free(top); 382 free(top);
356 top = 0; 383 top = 0;
357 ntop = 0; 384 ntop = 0;
358} 385}
359 386
387
360#if ENABLE_FEATURE_USE_TERMIOS 388#if ENABLE_FEATURE_USE_TERMIOS
361#include <termios.h> 389#include <termios.h>
362#include <signal.h> 390#include <signal.h>
363 391
364
365static struct termios initial_settings; 392static struct termios initial_settings;
366 393
367static void reset_term(void) 394static void reset_term(void)
@@ -427,7 +454,7 @@ int top_main(int argc, char **argv)
427#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 454#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
428 455
429 while (1) { 456 while (1) {
430 procps_status_t *p; 457 procps_status_t *p = NULL;
431 458
432 /* Default to 25 lines - 5 lines for status */ 459 /* Default to 25 lines - 5 lines for status */
433 lines = 24 - 3; 460 lines = 24 - 3;
@@ -442,11 +469,26 @@ int top_main(int argc, char **argv)
442#endif /* FEATURE_USE_TERMIOS */ 469#endif /* FEATURE_USE_TERMIOS */
443 470
444 /* read process IDs & status for all the processes */ 471 /* read process IDs & status for all the processes */
445 while ((p = procps_scan(0)) != 0) { 472 while ((p = procps_scan(p, 0
473 | PSSCAN_PID
474 | PSSCAN_PPID
475 | PSSCAN_RSS
476 | PSSCAN_STIME
477 | PSSCAN_UTIME
478 | PSSCAN_STATE
479 | PSSCAN_COMM
480 | PSSCAN_SID
481 | PSSCAN_UIDGID
482 ))) {
446 int n = ntop; 483 int n = ntop;
447 484 top = xrealloc(top, (++ntop)*sizeof(top_status_t));
448 top = xrealloc(top, (++ntop)*sizeof(procps_status_t)); 485 top[n].pid = p->pid;
449 memcpy(top + n, p, sizeof(procps_status_t)); 486 top[n].ppid = p->ppid;
487 top[n].rss = p->rss;
488 top[n].ticks = p->stime + p->utime;
489 top[n].uid = p->uid;
490 strcpy(top[n].state, p->state);
491 strcpy(top[n].comm, p->comm);
450 } 492 }
451 if (ntop == 0) { 493 if (ntop == 0) {
452 bb_error_msg_and_die("can't find process info in /proc"); 494 bb_error_msg_and_die("can't find process info in /proc");
@@ -459,9 +501,9 @@ int top_main(int argc, char **argv)
459 continue; 501 continue;
460 } 502 }
461 do_stats(); 503 do_stats();
462 qsort(top, ntop, sizeof(procps_status_t), (void*)mult_lvl_cmp); 504 qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
463#else 505#else
464 qsort(top, ntop, sizeof(procps_status_t), (void*)sort_function); 506 qsort(top, ntop, sizeof(top_status_t), (void*)sort_function);
465#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */ 507#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
466 count = lines; 508 count = lines;
467 if (OPT_BATCH_MODE || count > ntop) { 509 if (OPT_BATCH_MODE || count > ntop) {