diff options
author | Eric Andersen <andersen@codepoet.org> | 2002-09-30 20:52:10 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2002-09-30 20:52:10 +0000 |
commit | 08a72209c9b5cb74d851ef5c3db8c16eee6f657d (patch) | |
tree | baa06980ecaf8a8cedb27887f6da8b6d47e9f8c6 /procps/top.c | |
parent | 02e6ba91e887bd11146a57185b223582f56f3f09 (diff) | |
download | busybox-w32-08a72209c9b5cb74d851ef5c3db8c16eee6f657d.tar.gz busybox-w32-08a72209c9b5cb74d851ef5c3db8c16eee6f657d.tar.bz2 busybox-w32-08a72209c9b5cb74d851ef5c3db8c16eee6f657d.zip |
last_patch58 from vodz:
Ok. I generate patch for include to busybox-devel my work with
top (original author give me maintaining) and telnetd (my
support and unofficial maintaining) applets. Docs changes
also: added awk, netstat, time applets to list ;)
Diffstat (limited to 'procps/top.c')
-rw-r--r-- | procps/top.c | 875 |
1 files changed, 583 insertions, 292 deletions
diff --git a/procps/top.c b/procps/top.c index f6bbb23cd..06ae77119 100644 --- a/procps/top.c +++ b/procps/top.c | |||
@@ -1,60 +1,330 @@ | |||
1 | /* | 1 | /* |
2 | * A tiny 'top' utility. | 2 | * A tiny 'top' utility. |
3 | * | 3 | * |
4 | * This is written specifically for the linux /proc/<PID>/status | 4 | * This is written specifically for the linux /proc/<PID>/stat(m) |
5 | * file format, but it checks that the file actually conforms to the | 5 | * files format. |
6 | * format that this utility expects. | 6 | |
7 | 7 | * This reads the PIDs of all processes and their status and shows | |
8 | * This reads the PIDs of all processes at startup and then shows the | 8 | * the status of processes (first ones that fit to screen) at given |
9 | * status of those processes at given intervals. User can give | 9 | * intervals. |
10 | * maximum number of processes to show. If a process exits, it's PID | ||
11 | * is shown as 'EXIT'. If new processes are started while this works, | ||
12 | * it doesn't add them to the list of shown processes. | ||
13 | * | 10 | * |
14 | * NOTES: | 11 | * NOTES: |
15 | * - At startup this changes to /proc, all the reads are then | 12 | * - At startup this changes to /proc, all the reads are then |
16 | * relative to that. | 13 | * relative to that. |
17 | * - Includes code from the scandir() manual page. | ||
18 | * | ||
19 | * TODO: | ||
20 | * - ppid, uid etc could be read only once when program starts | ||
21 | * and rest of the information could be gotten from the | ||
22 | * /proc/<PID>/statm file. | ||
23 | * - Add process CPU and memory usage *percentages*. | ||
24 | * | 14 | * |
25 | * (C) Eero Tamminen <oak at welho dot com> | 15 | * (C) Eero Tamminen <oak at welho dot com> |
16 | * | ||
17 | * Rewroted by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru> | ||
18 | */ | ||
19 | |||
20 | /* Original code Copyrights */ | ||
21 | /* | ||
22 | * Copyright (c) 1992 Branko Lankester | ||
23 | * Copyright (c) 1992 Roger Binns | ||
24 | * Copyright (C) 1994-1996 Charles L. Blake. | ||
25 | * Copyright (C) 1992-1998 Michael K. Johnson | ||
26 | * May be distributed under the conditions of the | ||
27 | * GNU Library General Public License | ||
26 | */ | 28 | */ |
29 | |||
30 | #include <sys/types.h> | ||
27 | #include <stdio.h> | 31 | #include <stdio.h> |
28 | #include <stdlib.h> | 32 | #include <stdlib.h> |
29 | #include <unistd.h> | 33 | #include <unistd.h> |
30 | #include <dirent.h> | 34 | #include <dirent.h> |
31 | #include <string.h> | 35 | #include <string.h> |
32 | #include <sys/ioctl.h> | 36 | #include <sys/ioctl.h> |
37 | #include <sys/stat.h> | ||
38 | /* get page info */ | ||
39 | #include <asm/page.h> | ||
33 | #include "busybox.h" | 40 | #include "busybox.h" |
34 | 41 | ||
42 | //#define FEATURE_CPU_USAGE_PERCENTAGE /* + 2k */ | ||
43 | |||
44 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
45 | #include <time.h> | ||
46 | #include <sys/time.h> | ||
47 | #include <fcntl.h> | ||
48 | #include <netinet/in.h> /* htons */ | ||
49 | #endif | ||
50 | |||
35 | 51 | ||
36 | /* process information taken from /proc, | ||
37 | * The code takes into account how long the fields below are, | ||
38 | * starting from copying the file from 'status' file to displaying it! | ||
39 | */ | ||
40 | typedef struct { | 52 | typedef struct { |
41 | char uid[6]; /* User ID */ | 53 | int pid; |
42 | char pid[6]; /* Pid */ | 54 | char user[9]; |
43 | char ppid[6]; /* Parent Pid */ | 55 | char state[4]; |
44 | char name[12]; /* Name */ | 56 | unsigned long rss; |
45 | char cmd[20]; /* command line[read/show size] */ | 57 | int ppid; |
46 | char state[2]; /* State: S, W... */ | 58 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE |
47 | char size[9]; /* VmSize */ | 59 | unsigned pcpu; |
48 | char lck[9]; /* VmLck */ | 60 | unsigned long stime, utime; |
49 | char rss[9]; /* VmRSS */ | 61 | #endif |
50 | char data[9]; /* VmData */ | 62 | char *cmd; |
51 | char stk[9]; /* VmStk */ | 63 | |
52 | char exe[9]; /* VmExe */ | 64 | /* basename of executable file in call to exec(2), |
53 | char lib[9]; /* VmLib */ | 65 | size from kernel headers */ |
66 | char short_cmd[16]; | ||
54 | } status_t; | 67 | } status_t; |
55 | 68 | ||
69 | typedef int (*cmp_t)(status_t *P, status_t *Q); | ||
70 | |||
71 | static status_t *top; /* Hehe */ | ||
72 | static int ntop; | ||
73 | |||
74 | |||
75 | static int pid_sort (status_t *P, status_t *Q) | ||
76 | { | ||
77 | int p = P->pid; | ||
78 | int q = Q->pid; | ||
79 | |||
80 | if( p < q ) return -1; | ||
81 | if( p > q ) return 1; | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static int mem_sort (status_t *P, status_t *Q) | ||
86 | { | ||
87 | long p = P->rss; | ||
88 | long q = Q->rss; | ||
89 | |||
90 | if( p > q ) return -1; | ||
91 | if( p < q ) return 1; | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
96 | |||
97 | #define sort_depth 3 | ||
98 | static cmp_t sort_function[sort_depth]; | ||
99 | |||
100 | static int pcpu_sort (status_t *P, status_t *Q) | ||
101 | { | ||
102 | int p = P->pcpu; | ||
103 | int q = Q->pcpu; | ||
104 | |||
105 | if( p > q ) return -1; | ||
106 | if( p < q ) return 1; | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static int time_sort (status_t *P, status_t *Q) | ||
111 | { | ||
112 | long p = P->stime; | ||
113 | long q = Q->stime; | ||
114 | |||
115 | p += P->utime; | ||
116 | q += Q->utime; | ||
117 | if( p > q ) return -1; | ||
118 | if( p < q ) return 1; | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | int mult_lvl_cmp(void* a, void* b) { | ||
123 | int i, cmp_val; | ||
124 | |||
125 | for(i = 0; i < sort_depth; i++) { | ||
126 | cmp_val = (*sort_function[i])(a, b); | ||
127 | if (cmp_val != 0) | ||
128 | return cmp_val; | ||
129 | } | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | /* This structure stores some critical information from one frame to | ||
134 | the next. mostly used for sorting. Added cumulative and resident fields. */ | ||
135 | struct save_hist { | ||
136 | int ticks; | ||
137 | int pid; | ||
138 | int utime; | ||
139 | int stime; | ||
140 | }; | ||
141 | |||
142 | /* | ||
143 | * Calculates percent cpu usage for each task. | ||
144 | */ | ||
145 | |||
146 | static struct save_hist *save_history; | ||
147 | |||
148 | static unsigned long Hertz; | ||
149 | |||
150 | /*********************************************************************** | ||
151 | * Some values in /proc are expressed in units of 1/HZ seconds, where HZ | ||
152 | * is the kernel clock tick rate. One of these units is called a jiffy. | ||
153 | * The HZ value used in the kernel may vary according to hacker desire. | ||
154 | * According to Linus Torvalds, this is not true. He considers the values | ||
155 | * in /proc as being in architecture-dependant units that have no relation | ||
156 | * to the kernel clock tick rate. Examination of the kernel source code | ||
157 | * reveals that opinion as wishful thinking. | ||
158 | * | ||
159 | * In any case, we need the HZ constant as used in /proc. (the real HZ value | ||
160 | * may differ, but we don't care) There are several ways we could get HZ: | ||
161 | * | ||
162 | * 1. Include the kernel header file. If it changes, recompile this library. | ||
163 | * 2. Use the sysconf() function. When HZ changes, recompile the C library! | ||
164 | * 3. Ask the kernel. This is obviously correct... | ||
165 | * | ||
166 | * Linus Torvalds won't let us ask the kernel, because he thinks we should | ||
167 | * not know the HZ value. Oh well, we don't have to listen to him. | ||
168 | * Someone smuggled out the HZ value. :-) | ||
169 | * | ||
170 | * This code should work fine, even if Linus fixes the kernel to match his | ||
171 | * stated behavior. The code only fails in case of a partial conversion. | ||
172 | * | ||
173 | */ | ||
174 | |||
175 | #define FILE_TO_BUF(filename, fd) do{ \ | ||
176 | if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \ | ||
177 | perror_msg_and_die("/proc not be mounted?"); \ | ||
178 | } \ | ||
179 | lseek(fd, 0L, SEEK_SET); \ | ||
180 | if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ | ||
181 | perror_msg_and_die("%s", filename); \ | ||
182 | } \ | ||
183 | buf[local_n] = '\0'; \ | ||
184 | }while(0) | ||
185 | |||
186 | #define FILE_TO_BUF2(filename, fd) do{ \ | ||
187 | lseek(fd, 0L, SEEK_SET); \ | ||
188 | if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \ | ||
189 | perror_msg_and_die("%s", filename); \ | ||
190 | } \ | ||
191 | buf[local_n] = '\0'; \ | ||
192 | }while(0) | ||
193 | |||
194 | static void init_Hertz_value(void) { | ||
195 | unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */ | ||
196 | double up_1, up_2, seconds; | ||
197 | unsigned long jiffies, h; | ||
198 | char buf[80]; | ||
199 | int uptime_fd = -1; | ||
200 | int stat_fd = -1; | ||
201 | |||
202 | long smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF); | ||
203 | |||
204 | if(smp_num_cpus<1) smp_num_cpus=1; | ||
205 | do { | ||
206 | int local_n; | ||
207 | |||
208 | FILE_TO_BUF("uptime", uptime_fd); | ||
209 | up_1 = strtod(buf, 0); | ||
210 | FILE_TO_BUF("stat", stat_fd); | ||
211 | sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j); | ||
212 | FILE_TO_BUF2("uptime", uptime_fd); | ||
213 | up_2 = strtod(buf, 0); | ||
214 | } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */ | ||
215 | |||
216 | close(uptime_fd); | ||
217 | close(stat_fd); | ||
218 | |||
219 | jiffies = user_j + nice_j + sys_j + other_j; | ||
220 | seconds = (up_1 + up_2) / 2; | ||
221 | h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus ); | ||
222 | /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */ | ||
223 | switch(h){ | ||
224 | case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */ | ||
225 | case 48 ... 52 : Hertz = 50; break; | ||
226 | case 58 ... 62 : Hertz = 60; break; | ||
227 | case 63 ... 65 : Hertz = 64; break; /* StrongARM /Shark */ | ||
228 | case 95 ... 105 : Hertz = 100; break; /* normal Linux */ | ||
229 | case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */ | ||
230 | case 195 ... 204 : Hertz = 200; break; /* normal << 1 */ | ||
231 | case 253 ... 260 : Hertz = 256; break; | ||
232 | case 295 ... 304 : Hertz = 300; break; /* 3 cpus */ | ||
233 | case 393 ... 408 : Hertz = 400; break; /* normal << 2 */ | ||
234 | case 495 ... 504 : Hertz = 500; break; /* 5 cpus */ | ||
235 | case 595 ... 604 : Hertz = 600; break; /* 6 cpus */ | ||
236 | case 695 ... 704 : Hertz = 700; break; /* 7 cpus */ | ||
237 | case 790 ... 808 : Hertz = 800; break; /* normal << 3 */ | ||
238 | case 895 ... 904 : Hertz = 900; break; /* 9 cpus */ | ||
239 | case 990 ... 1010 : Hertz = 1000; break; /* ARM */ | ||
240 | case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */ | ||
241 | case 1095 ... 1104 : Hertz = 1100; break; /* 11 cpus */ | ||
242 | case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */ | ||
243 | default: | ||
244 | /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */ | ||
245 | Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | static void do_stats(void) | ||
250 | { | ||
251 | struct timeval t; | ||
252 | static struct timeval oldtime; | ||
253 | struct timezone timez; | ||
254 | float elapsed_time; | ||
255 | |||
256 | status_t *cur; | ||
257 | int total_time, i, n; | ||
258 | static int prev_count; | ||
259 | int systime, usrtime, pid; | ||
260 | |||
261 | struct save_hist *New_save_hist; | ||
262 | |||
263 | /* | ||
264 | * Finds the current time (in microseconds) and calculates the time | ||
265 | * elapsed since the last update. | ||
266 | */ | ||
267 | gettimeofday(&t, &timez); | ||
268 | elapsed_time = (t.tv_sec - oldtime.tv_sec) | ||
269 | + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0; | ||
270 | oldtime.tv_sec = t.tv_sec; | ||
271 | oldtime.tv_usec = t.tv_usec; | ||
272 | |||
273 | New_save_hist = alloca(sizeof(struct save_hist)*ntop); | ||
274 | /* | ||
275 | * Make a pass through the data to get stats. | ||
276 | */ | ||
277 | for(n = 0; n < ntop; n++) { | ||
278 | cur = top + n; | ||
279 | |||
280 | /* | ||
281 | * Calculate time in cur process. Time is sum of user time | ||
282 | * (usrtime) plus system time (systime). | ||
283 | */ | ||
284 | systime = cur->stime; | ||
285 | usrtime = cur->utime; | ||
286 | pid = cur->pid; | ||
287 | total_time = systime + usrtime; | ||
288 | New_save_hist[n].ticks = total_time; | ||
289 | New_save_hist[n].pid = pid; | ||
290 | New_save_hist[n].stime = systime; | ||
291 | New_save_hist[n].utime = usrtime; | ||
292 | |||
293 | /* find matching entry from previous pass */ | ||
294 | for (i = 0; i < prev_count; i++) { | ||
295 | if (save_history[i].pid == pid) { | ||
296 | total_time -= save_history[i].ticks; | ||
297 | systime -= save_history[i].stime; | ||
298 | usrtime -= save_history[i].utime; | ||
299 | break; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | /* | ||
304 | * Calculate percent cpu time for cur task. | ||
305 | */ | ||
306 | i = (total_time * 10 * 100/Hertz) / elapsed_time; | ||
307 | if (i > 999) | ||
308 | i = 999; | ||
309 | cur->pcpu = i; | ||
310 | |||
311 | } | ||
312 | |||
313 | /* | ||
314 | * Save cur frame's information. | ||
315 | */ | ||
316 | free(save_history); | ||
317 | save_history = memcpy(xmalloc(sizeof(struct save_hist)*n), New_save_hist, | ||
318 | sizeof(struct save_hist)*n); | ||
319 | prev_count = n; | ||
320 | qsort(top, n, sizeof(status_t), (void*)mult_lvl_cmp); | ||
321 | } | ||
322 | #else | ||
323 | static cmp_t sort_function; | ||
324 | #endif /* FEATURE_CPU_USAGE_PERCENTAGE */ | ||
325 | |||
56 | /* display generic info (meminfo / loadavg) */ | 326 | /* display generic info (meminfo / loadavg) */ |
57 | static void display_generic(void) | 327 | static unsigned long display_generic(void) |
58 | { | 328 | { |
59 | FILE *fp; | 329 | FILE *fp; |
60 | char buf[80]; | 330 | char buf[80]; |
@@ -62,303 +332,245 @@ static void display_generic(void) | |||
62 | unsigned long total, used, mfree, shared, buffers, cached; | 332 | unsigned long total, used, mfree, shared, buffers, cached; |
63 | 333 | ||
64 | /* read memory info */ | 334 | /* read memory info */ |
65 | fp = fopen("meminfo", "r"); | 335 | fp = xfopen("meminfo", "r"); |
66 | if (!fp) { | ||
67 | perror("fopen('meminfo')"); | ||
68 | return; | ||
69 | } | ||
70 | fgets(buf, sizeof(buf), fp); /* skip first line */ | 336 | fgets(buf, sizeof(buf), fp); /* skip first line */ |
71 | 337 | ||
72 | if (fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu", | 338 | if (fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu", |
73 | &total, &used, &mfree, &shared, &buffers, &cached) != 6) { | 339 | &total, &used, &mfree, &shared, &buffers, &cached) != 6) { |
74 | fprintf(stderr, "Error: failed to read 'meminfo'"); | 340 | error_msg_and_die("failed to read '%s'", "meminfo"); |
75 | fclose(fp); | ||
76 | } | 341 | } |
77 | fclose(fp); | 342 | fclose(fp); |
78 | 343 | ||
79 | /* read load average */ | 344 | /* read load average */ |
80 | fp = fopen("loadavg", "r"); | 345 | fp = xfopen("loadavg", "r"); |
81 | if (!fp) { | ||
82 | perror("fopen('loadavg')"); | ||
83 | return; | ||
84 | } | ||
85 | if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) { | 346 | if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) { |
86 | fprintf(stderr, "Error: failed to read 'loadavg'"); | 347 | error_msg_and_die("failed to read '%s'", "loadavg"); |
87 | fclose(fp); | ||
88 | return; | ||
89 | } | 348 | } |
90 | fclose(fp); | 349 | fclose(fp); |
91 | 350 | ||
92 | /* convert to kilobytes */ | 351 | /* convert to kilobytes */ |
93 | if (total) total /= 1024; | 352 | used /= 1024; |
94 | if (used) used /= 1024; | 353 | mfree /= 1024; |
95 | if (mfree) mfree /= 1024; | 354 | shared /= 1024; |
96 | if (shared) shared /= 1024; | 355 | buffers /= 1024; |
97 | if (buffers) buffers /= 1024; | 356 | cached /= 1024; |
98 | if (cached) cached /= 1024; | ||
99 | 357 | ||
100 | /* output memory info and load average */ | 358 | /* output memory info and load average */ |
101 | printf("Mem: %ldK, %ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n", | 359 | /* clear screen & go to top */ |
102 | total, used, mfree, shared, buffers, cached); | 360 | printf("\e[H\e[J" "Mem: " |
103 | printf("Load average: %.2f, %.2f, %.2f (State: S=sleeping R=running, W=waiting)\n", | 361 | "%ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n", |
362 | used, mfree, shared, buffers, cached); | ||
363 | printf("Load average: %.2f, %.2f, %.2f " | ||
364 | "(State: S=sleeping R=running, W=waiting)\n", | ||
104 | avg1, avg2, avg3); | 365 | avg1, avg2, avg3); |
366 | return total / 1024; | ||
105 | } | 367 | } |
106 | 368 | ||
107 | 369 | ||
108 | /* display process statuses */ | 370 | /* display process statuses */ |
109 | static void display_status(int count, const status_t *s) | 371 | static void display_status(int count, int col) |
110 | { | 372 | { |
111 | const char *fmt, *cmd; | 373 | status_t *s = top; |
112 | 374 | char rss_str_buf[8]; | |
113 | /* clear screen & go to top */ | 375 | unsigned long total_memory = display_generic(); |
114 | printf("\e[2J\e[1;1H"); | ||
115 | |||
116 | display_generic(); | ||
117 | 376 | ||
377 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
118 | /* what info of the processes is shown */ | 378 | /* what info of the processes is shown */ |
119 | printf("\n%*s %*s %*s %*s %*s %*s %-*s\n", | 379 | printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%MEM COMMAND\e[0m\n"); |
120 | sizeof(s->pid)-1, "Pid:", | 380 | #else |
121 | sizeof(s->state)-1, "", | 381 | printf("\n\e[7m PID USER STATUS RSS PPID %%MEM COMMAND\e[0m\n"); |
122 | sizeof(s->ppid)-1, "PPid:", | 382 | #endif |
123 | sizeof(s->uid)-1, "UID:", | ||
124 | sizeof(s->size)-1, "WmSize:", | ||
125 | sizeof(s->rss)-1, "WmRSS:", | ||
126 | sizeof(s->cmd)-1, "command line:"); | ||
127 | 383 | ||
128 | while (count--) { | 384 | while (count--) { |
129 | if (s->cmd[0]) { | 385 | char *namecmd = s->cmd; |
130 | /* normal process, has command line */ | 386 | int pmem; |
131 | cmd = s->cmd; | ||
132 | fmt = "%*s %*s %*s %*s %*s %*s %s\n"; | ||
133 | } else { | ||
134 | /* no command line, show only process name */ | ||
135 | cmd = s->name; | ||
136 | fmt = "%*s %*s %*s %*s %*s %*s [%s]\n"; | ||
137 | } | ||
138 | printf(fmt, | ||
139 | sizeof(s->pid)-1, s->pid, | ||
140 | sizeof(s->state)-1, s->state, | ||
141 | sizeof(s->ppid)-1, s->ppid, | ||
142 | sizeof(s->uid)-1, s->uid, | ||
143 | sizeof(s->size)-1, s->size, | ||
144 | sizeof(s->rss)-1, s->rss, | ||
145 | cmd); | ||
146 | s++; | ||
147 | } | ||
148 | } | ||
149 | 387 | ||
150 | 388 | pmem = 1000.0 * s->rss / total_memory; | |
151 | /* checks if given 'buf' for process starts with 'id' + ':' + TAB | 389 | if (pmem > 999) pmem = 999; |
152 | * and stores rest of the buf to 'store' with max size 'size' | ||
153 | */ | ||
154 | static int process_status(const char *buf, const char *id, char *store, size_t size) | ||
155 | { | ||
156 | int len, i; | ||
157 | 390 | ||
158 | /* check status field name */ | 391 | if(s->rss > 10*1024) |
159 | len = strlen(id); | 392 | sprintf(rss_str_buf, "%6ldM", s->rss/1024); |
160 | if (strncmp(buf, id, len) != 0) { | ||
161 | if(store) | ||
162 | error_msg_and_die("ERROR status: line doesn't start with '%s' in:\n%s\n", id, buf); | ||
163 | else | 393 | else |
164 | return 0; | 394 | sprintf(rss_str_buf, "%7ld", s->rss); |
165 | } | 395 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE |
166 | if (!store) { | 396 | printf("%5d %-8s %s %s %5d %2d.%d %2u.%u ", |
167 | /* ignoring this field */ | 397 | #else |
168 | return 1; | 398 | printf("%5d %-8s %s %s %5d %2u.%u ", |
169 | } | 399 | #endif |
170 | buf += len; | 400 | s->pid, s->user, s->state, rss_str_buf, s->ppid, |
171 | 401 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | |
172 | /* check status field format */ | 402 | s->pcpu/10, s->pcpu%10, |
173 | if ((*buf++ != ':') || (*buf++ != '\t')) { | 403 | #endif |
174 | error_msg_and_die("ERROR status: field '%s' not followed with ':' + TAB in:\n%s\n", id, buf); | 404 | pmem/10, pmem%10); |
175 | } | 405 | if(namecmd != 0 && namecmd[0] != 0) { |
176 | 406 | if(strlen(namecmd) > col) | |
177 | /* skip whitespace in Wm* fields */ | 407 | namecmd[col] = 0; |
178 | if (id[0] == 'V' && id[1] == 'm') { | 408 | printf("%s\n", namecmd); |
179 | i = 3; | ||
180 | while (i--) { | ||
181 | if (*buf == ' ') { | ||
182 | buf++; | ||
183 | } else { | 409 | } else { |
184 | error_msg_and_die("ERROR status: can't skip whitespace for " | 410 | namecmd = s->short_cmd; |
185 | "'%s' field in:\n%s\n", id, buf); | 411 | if(strlen(namecmd) > (col-2)) |
412 | namecmd[col-2] = 0; | ||
413 | printf("[%s]\n", namecmd); | ||
186 | } | 414 | } |
415 | s++; | ||
187 | } | 416 | } |
188 | } | ||
189 | |||
190 | /* copy at max (size-1) chars and force '\0' to the end */ | ||
191 | while (--size) { | ||
192 | if (*buf < ' ') { | ||
193 | break; | ||
194 | } | ||
195 | *store++ = *buf++; | ||
196 | } | ||
197 | *store = '\0'; | ||
198 | return 1; | ||
199 | } | 417 | } |
200 | 418 | ||
201 | /* read process statuses */ | 419 | /* returns true for file names which are PID dirs |
202 | static void read_status(int num, status_t *s) | 420 | * (i.e. start with number) |
421 | */ | ||
422 | static int filter_pids(const struct dirent *dir) | ||
203 | { | 423 | { |
424 | char *name = dir->d_name; | ||
425 | int n; | ||
204 | char status[20]; | 426 | char status[20]; |
205 | char buf[80]; | 427 | char buf[1024]; |
206 | FILE *fp; | 428 | FILE *fp; |
429 | status_t curstatus; | ||
430 | int pid; | ||
431 | long tasknice; | ||
432 | struct stat sb; | ||
433 | |||
434 | if (!(*name >= '0' && *name <= '9')) | ||
435 | return 0; | ||
436 | if(stat(name, &sb)) | ||
437 | return 0; | ||
438 | |||
439 | memset(&curstatus, 0, sizeof(status_t)); | ||
440 | pid = atoi(name); | ||
441 | curstatus.pid = pid; | ||
207 | 442 | ||
208 | while (num--) { | 443 | my_getpwuid(curstatus.user, sb.st_uid); |
209 | sprintf(status, "%s/status", s->pid); | 444 | |
210 | 445 | sprintf(status, "%d/stat", pid); | |
211 | /* read the command line from 'cmdline' in PID dir */ | 446 | if((fp = fopen(status, "r")) == NULL) |
212 | fp = fopen(status, "r"); | 447 | return 0; |
213 | if (!fp) { | 448 | name = fgets(buf, sizeof(buf), fp); |
214 | strncpy(s->pid, "EXIT", sizeof(s->pid)); | ||
215 | s->pid[sizeof(s->pid)-1] = '\0'; | ||
216 | fclose(fp); | 449 | fclose(fp); |
217 | continue; | 450 | if(name == NULL) |
218 | } | 451 | return 0; |
452 | name = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */ | ||
453 | if(name == 0 || name[1] != ' ') | ||
454 | return 0; | ||
455 | *name = 0; | ||
456 | sscanf(buf, "%*s (%15c", curstatus.short_cmd); | ||
457 | n = sscanf(name+2, | ||
458 | "%c %d " | ||
459 | "%*s %*s %*s %*s " /* pgrp, session, tty, tpgid */ | ||
460 | "%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */ | ||
461 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
462 | "%lu %lu " | ||
463 | #else | ||
464 | "%*s %*s " | ||
465 | #endif | ||
466 | "%*s %*s %*s " /* cutime, cstime, priority */ | ||
467 | "%ld " | ||
468 | "%*s %*s %*s " /* timeout, it_real_value, start_time */ | ||
469 | "%*s " /* vsize */ | ||
470 | "%ld", | ||
471 | curstatus.state, &curstatus.ppid, | ||
472 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
473 | &curstatus.utime, &curstatus.stime, | ||
474 | #endif | ||
475 | &tasknice, | ||
476 | &curstatus.rss); | ||
477 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
478 | if(n != 6) | ||
479 | #else | ||
480 | if(n != 4) | ||
481 | #endif | ||
482 | return 0; | ||
483 | |||
484 | if (curstatus.rss == 0 && curstatus.state[0] != 'Z') | ||
485 | curstatus.state[1] = 'W'; | ||
486 | else | ||
487 | curstatus.state[1] = ' '; | ||
488 | if (tasknice < 0) | ||
489 | curstatus.state[2] = '<'; | ||
490 | else if (tasknice > 0) | ||
491 | curstatus.state[2] = 'N'; | ||
492 | else | ||
493 | curstatus.state[2] = ' '; | ||
494 | |||
495 | curstatus.rss <<= (PAGE_SHIFT - 10); /* 2**10 = 1kb */ | ||
219 | 496 | ||
220 | /* get and process the information */ | 497 | sprintf(status, "%d/cmdline", pid); |
221 | fgets(buf, sizeof(buf), fp); | 498 | if((fp = fopen(status, "r")) == NULL) |
222 | process_status(buf, "Name", s->name, sizeof(s->name)); | 499 | return 0; |
223 | fgets(buf, sizeof(buf), fp); | 500 | if(fgets(buf, sizeof(buf), fp) != NULL) { |
224 | process_status(buf, "State", s->state, sizeof(s->state)); | 501 | name = strchr(buf, '\n'); |
225 | fgets(buf, sizeof(buf), fp); | 502 | if(name != NULL) |
226 | if(process_status(buf, "Tgid", NULL, 0)) | 503 | *name = 0; |
227 | fgets(buf, sizeof(buf), fp); | 504 | if(buf[0]) |
228 | process_status(buf, "Pid", NULL, 0); | 505 | curstatus.cmd = strdup(buf); /* if NULL it work true also */ |
229 | fgets(buf, sizeof(buf), fp); | ||
230 | process_status(buf, "PPid", s->ppid, sizeof(s->ppid)); | ||
231 | fgets(buf, sizeof(buf), fp); | ||
232 | if(process_status(buf, "TracerPid", NULL, 0)) | ||
233 | fgets(buf, sizeof(buf), fp); | ||
234 | process_status(buf, "Uid", s->uid, sizeof(s->uid)); | ||
235 | fgets(buf, sizeof(buf), fp); | ||
236 | process_status(buf, "Gid", NULL, 0); | ||
237 | fgets(buf, sizeof(buf), fp); | ||
238 | if(process_status(buf, "FDSize", NULL, 0)) | ||
239 | fgets(buf, sizeof(buf), fp); | ||
240 | process_status(buf, "Groups", NULL, 0); | ||
241 | fgets(buf, sizeof(buf), fp); | ||
242 | /* only user space processes have command line | ||
243 | * and memory statistics | ||
244 | */ | ||
245 | if (s->cmd[0]) { | ||
246 | process_status(buf, "VmSize", s->size, sizeof(s->size)); | ||
247 | fgets(buf, sizeof(buf), fp); | ||
248 | process_status(buf, "VmLck", s->lck, sizeof(s->lck)); | ||
249 | fgets(buf, sizeof(buf), fp); | ||
250 | process_status(buf, "VmRSS", s->rss, sizeof(s->rss)); | ||
251 | fgets(buf, sizeof(buf), fp); | ||
252 | process_status(buf, "VmData", s->data, sizeof(s->data)); | ||
253 | fgets(buf, sizeof(buf), fp); | ||
254 | process_status(buf, "VmStk", s->stk, sizeof(s->stk)); | ||
255 | fgets(buf, sizeof(buf), fp); | ||
256 | process_status(buf, "VmExe", s->exe, sizeof(s->exe)); | ||
257 | fgets(buf, sizeof(buf), fp); | ||
258 | process_status(buf, "VmLib", s->lib, sizeof(s->lib)); | ||
259 | } | 506 | } |
260 | fclose(fp); | 507 | fclose(fp); |
261 | 508 | ||
262 | /* next process */ | 509 | n = ntop; |
263 | s++; | 510 | top = xrealloc(top, (++ntop)*sizeof(status_t)); |
264 | } | 511 | memcpy(top + n, &curstatus, sizeof(status_t)); |
512 | return 1; | ||
265 | } | 513 | } |
266 | 514 | ||
267 | 515 | ||
268 | /* allocs statuslist and reads process command lines, frees namelist, | 516 | static struct dirent **namelist; |
269 | * returns filled statuslist or NULL in case of error. | ||
270 | */ | ||
271 | static status_t *read_info(int num, struct dirent **namelist) | ||
272 | { | ||
273 | status_t *statuslist, *s; | ||
274 | char cmdline[20]; | ||
275 | FILE *fp; | ||
276 | int idx; | ||
277 | 517 | ||
278 | /* allocate & zero status for each of the processes */ | 518 | static void clearmems(void) { |
279 | statuslist = calloc(num, sizeof(status_t)); | 519 | int i; |
280 | if (!statuslist) { | 520 | |
281 | return NULL; | 521 | for(i = 0; i < ntop; i++) { |
282 | } | 522 | free(top[i].cmd); |
283 | 523 | free(namelist[i]); | |
284 | /* go through the processes */ | ||
285 | for (idx = 0; idx < num; idx++) { | ||
286 | |||
287 | /* copy PID string to status struct and free name */ | ||
288 | s = &(statuslist[idx]); | ||
289 | if (strlen(namelist[idx]->d_name) > sizeof(s->pid)-1) { | ||
290 | fprintf(stderr, "PID '%s' too long\n", namelist[idx]->d_name); | ||
291 | return NULL; | ||
292 | } | ||
293 | strncpy(s->pid, namelist[idx]->d_name, sizeof(s->pid)); | ||
294 | s->pid[sizeof(s->pid)-1] = '\0'; | ||
295 | free(namelist[idx]); | ||
296 | |||
297 | /* read the command line from 'cmdline' in PID dir */ | ||
298 | sprintf(cmdline, "%s/cmdline", s->pid); | ||
299 | fp = fopen(cmdline, "r"); | ||
300 | if (!fp) { | ||
301 | fclose(fp); | ||
302 | perror("fopen('cmdline')"); | ||
303 | return NULL; | ||
304 | } | ||
305 | fgets(statuslist[idx].cmd, sizeof(statuslist[idx].cmd), fp); | ||
306 | fclose(fp); | ||
307 | } | 524 | } |
525 | free(top); | ||
308 | free(namelist); | 526 | free(namelist); |
309 | return statuslist; | 527 | top = 0; |
528 | namelist = 0; | ||
529 | ntop = 0; | ||
310 | } | 530 | } |
311 | 531 | ||
532 | #if defined CONFIG_FEATURE_USE_TERMIOS | ||
533 | #include <termios.h> | ||
534 | #include <sys/time.h> | ||
535 | #include <signal.h> | ||
312 | 536 | ||
313 | /* returns true for file names which are PID dirs | ||
314 | * (i.e. start with number) | ||
315 | */ | ||
316 | static int filter_pids(const struct dirent *dir) | ||
317 | { | ||
318 | status_t dummy; | ||
319 | char *name = dir->d_name; | ||
320 | |||
321 | if (*name >= '0' && *name <= '9') { | ||
322 | if (strlen(name) > sizeof(dummy.pid)-1) { | ||
323 | fprintf(stderr, "PID name '%s' too long\n", name); | ||
324 | return 0; | ||
325 | } | ||
326 | return 1; | ||
327 | } | ||
328 | return 0; | ||
329 | } | ||
330 | 537 | ||
538 | static struct termios initial_settings; | ||
331 | 539 | ||
332 | /* compares two directory entry names as numeric strings | 540 | static void reset_term(void) |
333 | */ | ||
334 | static int num_sort(const void *a, const void *b) | ||
335 | { | 541 | { |
336 | int ia = atoi((*(struct dirent **)a)->d_name); | 542 | tcsetattr(0, TCSANOW, (void *) &initial_settings); |
337 | int ib = atoi((*(struct dirent **)b)->d_name); | 543 | #ifdef CONFIG_FEATURE_CLEAN_UP |
544 | clearmems(); | ||
545 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
546 | free(save_history); | ||
547 | #endif | ||
548 | #endif /* CONFIG_FEATURE_CLEAN_UP */ | ||
549 | } | ||
338 | 550 | ||
339 | if (ia == ib) { | 551 | static void sig_catcher (int sig) |
340 | return 0; | 552 | { |
341 | } | 553 | reset_term(); |
342 | /* NOTE: by switching the check, you change the process sort order */ | ||
343 | if (ia < ib) { | ||
344 | return -1; | ||
345 | } else { | ||
346 | return 1; | ||
347 | } | ||
348 | } | 554 | } |
555 | #endif /* CONFIG_FEATURE_USE_TERMIOS */ | ||
556 | |||
349 | 557 | ||
350 | int top_main(int argc, char **argv) | 558 | int top_main(int argc, char **argv) |
351 | { | 559 | { |
352 | status_t *statuslist; | 560 | int opt, interval, lines, col; |
353 | struct dirent **namelist; | 561 | #if defined CONFIG_FEATURE_USE_TERMIOS |
354 | int opt, num, interval, lines; | 562 | struct termios new_settings; |
355 | #if defined CONFIG_FEATURE_AUTOWIDTH && defined CONFIG_FEATURE_USE_TERMIOS | 563 | struct timeval tv; |
564 | fd_set readfds; | ||
565 | unsigned char c; | ||
566 | struct sigaction sa; | ||
567 | #if defined CONFIG_FEATURE_AUTOWIDTH | ||
356 | struct winsize win = { 0, 0, 0, 0 }; | 568 | struct winsize win = { 0, 0, 0, 0 }; |
357 | #endif | 569 | #endif |
570 | #endif /* CONFIG_FEATURE_USE_TERMIOS */ | ||
571 | |||
358 | /* Default update rate is 5 seconds */ | 572 | /* Default update rate is 5 seconds */ |
359 | interval = 5; | 573 | interval = 5; |
360 | /* Default to 25 lines - 5 lines for status */ | ||
361 | lines = 25 - 5; | ||
362 | 574 | ||
363 | /* do normal option parsing */ | 575 | /* do normal option parsing */ |
364 | while ((opt = getopt(argc, argv, "d:")) > 0) { | 576 | while ((opt = getopt(argc, argv, "d:")) > 0) { |
@@ -371,42 +583,121 @@ int top_main(int argc, char **argv) | |||
371 | } | 583 | } |
372 | } | 584 | } |
373 | 585 | ||
374 | #if defined CONFIG_FEATURE_AUTOWIDTH && defined CONFIG_FEATURE_USE_TERMIOS | 586 | /* Default to 25 lines - 5 lines for status */ |
375 | ioctl(fileno(stdout), TIOCGWINSZ, &win); | 587 | lines = 25 - 5; |
376 | if (win.ws_row > 4) | 588 | /* Default CMD format size */ |
377 | lines = win.ws_row - 5; | 589 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE |
590 | col = 35 - 6; | ||
591 | #else | ||
592 | col = 35; | ||
378 | #endif | 593 | #endif |
379 | |||
380 | /* change to proc */ | 594 | /* change to proc */ |
381 | if (chdir("/proc") < 0) { | 595 | if (chdir("/proc") < 0) { |
382 | perror_msg_and_die("chdir('/proc')"); | 596 | perror_msg_and_die("chdir('/proc')"); |
383 | } | 597 | } |
384 | 598 | #if defined CONFIG_FEATURE_USE_TERMIOS | |
385 | /* read process IDs for all the processes from the procfs */ | 599 | tcgetattr(0, (void *) &initial_settings); |
386 | num = scandir(".", &namelist, filter_pids, num_sort); | 600 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); |
387 | if (num < 0) { | 601 | new_settings.c_lflag &= ~(ISIG | ICANON); /* unbuffered input */ |
602 | /* Turn off echoing */ | ||
603 | new_settings.c_lflag &= ~(ECHO | ECHONL); | ||
604 | |||
605 | signal (SIGTERM, sig_catcher); | ||
606 | sigaction (SIGTERM, (struct sigaction *) 0, &sa); | ||
607 | sa.sa_flags |= SA_RESTART; | ||
608 | sa.sa_flags &= ~SA_INTERRUPT; | ||
609 | sigaction (SIGTERM, &sa, (struct sigaction *) 0); | ||
610 | sigaction (SIGINT, &sa, (struct sigaction *) 0); | ||
611 | tcsetattr(0, TCSANOW, (void *) &new_settings); | ||
612 | atexit(reset_term); | ||
613 | #if defined CONFIG_FEATURE_AUTOWIDTH | ||
614 | ioctl(0, TIOCGWINSZ, &win); | ||
615 | if (win.ws_row > 4) { | ||
616 | lines = win.ws_row - 5; | ||
617 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
618 | col = win.ws_col - 80 + 35 - 6; | ||
619 | #else | ||
620 | col = win.ws_col - 80 + 35; | ||
621 | #endif | ||
622 | } | ||
623 | #endif | ||
624 | #endif /* CONFIG_FEATURE_USE_TERMIOS */ | ||
625 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
626 | sort_function[0] = pcpu_sort; | ||
627 | sort_function[1] = mem_sort; | ||
628 | sort_function[2] = time_sort; | ||
629 | #else | ||
630 | sort_function = mem_sort; | ||
631 | #endif | ||
632 | while (1) { | ||
633 | /* read process IDs & status for all the processes */ | ||
634 | if (scandir(".", &namelist, filter_pids, 0) < 0) { | ||
388 | perror_msg_and_die("scandir('/proc')"); | 635 | perror_msg_and_die("scandir('/proc')"); |
389 | } | 636 | } |
390 | if (lines > num) { | 637 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE |
391 | lines = num; | 638 | if(!Hertz) { |
639 | init_Hertz_value(); | ||
640 | do_stats(); | ||
641 | sleep(1); | ||
642 | clearmems(); | ||
643 | continue; | ||
392 | } | 644 | } |
393 | 645 | do_stats(); | |
394 | /* read command line for each of the processes */ | 646 | #else |
395 | statuslist = read_info(num, namelist); | 647 | qsort(top, ntop, sizeof(status_t), (void*)sort_function); |
396 | if (!statuslist) { | 648 | #endif |
649 | opt = lines; | ||
650 | if (opt > ntop) { | ||
651 | opt = ntop; | ||
652 | } | ||
653 | /* show status for each of the processes */ | ||
654 | display_status(opt, col); | ||
655 | #if defined CONFIG_FEATURE_USE_TERMIOS | ||
656 | tv.tv_sec = interval; | ||
657 | tv.tv_usec = 0; | ||
658 | FD_ZERO (&readfds); | ||
659 | FD_SET (0, &readfds); | ||
660 | select (1, &readfds, NULL, NULL, &tv); | ||
661 | if (FD_ISSET (0, &readfds)) { | ||
662 | if (read (0, &c, 1) <= 0) { /* signal */ | ||
397 | return EXIT_FAILURE; | 663 | return EXIT_FAILURE; |
398 | } | 664 | } |
399 | 665 | if(c == 'q' || c == initial_settings.c_cc[VINTR]) | |
400 | while (1) { | 666 | return EXIT_SUCCESS; |
401 | /* read status for each of the processes */ | 667 | if(c == 'M') { |
402 | read_status(num, statuslist); | 668 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE |
403 | 669 | sort_function[0] = mem_sort; | |
404 | /* display status */ | 670 | sort_function[1] = pcpu_sort; |
405 | display_status(lines, statuslist); | 671 | sort_function[2] = time_sort; |
406 | 672 | #else | |
673 | sort_function = mem_sort; | ||
674 | #endif | ||
675 | } | ||
676 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
677 | if(c == 'P') { | ||
678 | sort_function[0] = pcpu_sort; | ||
679 | sort_function[1] = mem_sort; | ||
680 | sort_function[2] = time_sort; | ||
681 | } | ||
682 | if(c == 'T') { | ||
683 | sort_function[0] = time_sort; | ||
684 | sort_function[1] = mem_sort; | ||
685 | sort_function[2] = pcpu_sort; | ||
686 | } | ||
687 | #endif | ||
688 | if(c == 'N') { | ||
689 | #ifdef FEATURE_CPU_USAGE_PERCENTAGE | ||
690 | sort_function[0] = pid_sort; | ||
691 | #else | ||
692 | sort_function = pid_sort; | ||
693 | #endif | ||
694 | } | ||
695 | } | ||
696 | #else | ||
407 | sleep(interval); | 697 | sleep(interval); |
698 | #endif /* CONFIG_FEATURE_USE_TERMIOS */ | ||
699 | clearmems(); | ||
408 | } | 700 | } |
409 | 701 | ||
410 | free(statuslist); | ||
411 | return EXIT_SUCCESS; | 702 | return EXIT_SUCCESS; |
412 | } | 703 | } |