diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-05 00:43:51 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-11-05 00:43:51 +0000 |
commit | 459e4d6cf77940977a064edab60c7162731554fb (patch) | |
tree | 5deb7d2b82c280440761cceb4281738867d5631a | |
parent | fa07680091d20f9da1f8fa2c145dd92b5d62ae09 (diff) | |
download | busybox-w32-459e4d6cf77940977a064edab60c7162731554fb.tar.gz busybox-w32-459e4d6cf77940977a064edab60c7162731554fb.tar.bz2 busybox-w32-459e4d6cf77940977a064edab60c7162731554fb.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
-rw-r--r-- | include/libbb.h | 48 | ||||
-rw-r--r-- | libbb/find_pid_by_name.c | 6 | ||||
-rw-r--r-- | libbb/procps.c | 261 | ||||
-rw-r--r-- | procps/kill.c | 6 | ||||
-rw-r--r-- | procps/ps.c | 28 | ||||
-rw-r--r-- | procps/top.c | 144 |
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 |
503 | typedef struct { | 503 | typedef 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; |
518 | procps_status_t* procps_scan(int save_user_arg0); | 521 | enum { |
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 | }; | ||
538 | procps_status_t* alloc_procps_scan(int flags); | ||
539 | void free_procps_scan(procps_status_t* sp); | ||
540 | procps_status_t* procps_scan(procps_status_t* sp, int flags); | ||
519 | pid_t *find_pid_by_name(const char* procName); | 541 | pid_t *find_pid_by_name(const char* procName); |
520 | pid_t *pidlist_reverse(pid_t *pidList); | 542 | pid_t *pidlist_reverse(pid_t *pidList); |
543 | void clear_username_cache(void); | ||
544 | const char* get_cached_username(uid_t uid); | ||
521 | 545 | ||
522 | 546 | ||
523 | extern const char bb_uuenc_tbl_base64[]; | 547 | extern 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 | ||
14 | typedef struct { | ||
15 | uid_t uid; | ||
16 | char username[12]; | ||
17 | } user_map_t; | ||
18 | |||
19 | static user_map_t *username_cache; | ||
20 | static int username_cache_size; | ||
21 | |||
22 | void clear_username_cache(void) | ||
23 | { | ||
24 | free(username_cache); | ||
25 | username_cache = NULL; | ||
26 | username_cache_size = 0; | ||
27 | } | ||
28 | |||
29 | const 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 | ||
16 | static int read_to_buf(const char *filename, void *buf) | 45 | static 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 | ||
53 | procps_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 | ||
25 | procps_status_t * procps_scan(int save_user_arg0) | 60 | void 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 | ||
67 | void BUG_comm_size(void); | ||
68 | procps_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, | ||
219 | the 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 | ||
12 | int ps_main(int argc, char **argv) | 12 | int 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 | ||
33 | typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q); | ||
34 | 33 | ||
35 | static procps_status_t *top; /* Hehe */ | 34 | typedef 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; | ||
45 | static top_status_t *top; | ||
36 | static int ntop; | 46 | static int ntop; |
47 | /* This structure stores some critical information from one frame to | ||
48 | the next. Used for finding deltas. */ | ||
49 | struct save_hist { | ||
50 | unsigned long ticks; | ||
51 | unsigned pid; | ||
52 | }; | ||
53 | static struct save_hist *prev_hist; | ||
54 | static int prev_hist_count; | ||
55 | /* static int hist_iterations; */ | ||
56 | static 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 |
40 | static int pid_sort(procps_status_t *P, procps_status_t *Q) | 63 | static 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 | ||
46 | static int mem_sort(procps_status_t *P, procps_status_t *Q) | 71 | static 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 | |
79 | typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q); | ||
80 | |||
81 | #if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | ||
82 | |||
83 | static cmp_funcp sort_function; | ||
84 | |||
85 | #else | ||
52 | 86 | ||
53 | enum { SORT_DEPTH = 3 }; | 87 | enum { SORT_DEPTH = 3 }; |
54 | static cmp_t sort_function[SORT_DEPTH]; | ||
55 | 88 | ||
56 | static int pcpu_sort(procps_status_t *P, procps_status_t *Q) | 89 | static cmp_funcp sort_function[SORT_DEPTH]; |
90 | |||
91 | static 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 | ||
61 | static int time_sort(procps_status_t *P, procps_status_t *Q) | 98 | static 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 | ||
66 | static int mult_lvl_cmp(void* a, void* b) { | 105 | static 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. */ | ||
79 | struct save_hist { | ||
80 | int ticks; | ||
81 | pid_t pid; | ||
82 | }; | ||
83 | |||
84 | /* | ||
85 | * Calculates percent cpu usage for each task. | ||
86 | */ | ||
87 | |||
88 | static struct save_hist *prev_hist; | ||
89 | static int prev_hist_count; | ||
90 | /* static int hist_iterations; */ | ||
91 | |||
92 | |||
93 | static unsigned total_pcpu; | ||
94 | /* static unsigned long total_rss; */ | ||
95 | 116 | ||
96 | typedef struct { | 117 | typedef 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 | |||
118 | static void do_stats(void) | 140 | static 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 | ||
171 | static 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) */ |
175 | static unsigned long display_generic(int scr_width) | 195 | static 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 | |||
353 | static void clearmems(void) | 379 | static 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 | |||
365 | static struct termios initial_settings; | 392 | static struct termios initial_settings; |
366 | 393 | ||
367 | static void reset_term(void) | 394 | static 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) { |