Mercurial > hg > toybox
annotate toys/pending/top.c @ 1396:e0c9c5424864 draft
Isaac Dunham spotted that dhcp was also reimplementing daemon().
author | Rob Landley <rob@landley.net> |
---|---|
date | Sun, 20 Jul 2014 21:34:49 -0500 |
parents | 0ecfaa7022e8 |
children |
rev | line source |
---|---|
1079 | 1 /* top.c - Provide a view of process activity in real time. |
2 * | |
3 * Copyright 2013 Bilal Qureshi <bilal.jmi@gmail.com> | |
4 * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com> | |
5 * Copyright 2013 Kyungwan Han <asura321@gmail.com> | |
6 * | |
7 * No Standard | |
8 | |
9 USE_TOP(NEWTOY(top, ">0d#=3n#<1mb", TOYFLAG_USR|TOYFLAG_BIN)) | |
10 | |
11 config TOP | |
12 bool "top" | |
13 default n | |
14 help | |
15 | |
1265
0ecfaa7022e8
usage: is lower case (the help generator looks for that, might as well be consistent).
Rob Landley <rob@landley.net>
parents:
1079
diff
changeset
|
16 usage: top [-mb] [ -d seconds ] [ -n iterations ] |
1079 | 17 |
18 Provide a view of process activity in real time. | |
19 Keys | |
20 N/M/P/T show CPU usage, sort by pid/mem/cpu/time | |
21 S show memory | |
22 R reverse sort | |
23 H toggle threads | |
24 C,1 toggle SMP | |
25 Q,^C exit | |
26 | |
27 Options | |
28 -n Iterations before exiting | |
29 -d Delay between updates | |
30 -m Same as 's' key | |
31 -b Batch mode | |
32 */ | |
33 | |
34 #define FOR_top | |
35 #include "toys.h" | |
36 #include <signal.h> | |
37 #include <poll.h> | |
38 | |
39 GLOBALS( | |
40 long iterations; | |
41 long delay; | |
42 | |
43 long cmp_field; | |
44 long reverse; | |
45 long rows; | |
46 long smp; | |
47 long threads; | |
48 long m_flag; | |
49 long num_new_procs; | |
50 long scroll_offset; | |
51 struct termios inf; | |
52 ) | |
53 | |
54 #define PROC_NAME_LEN 512 //For long cmdline. | |
55 #define INIT_PROCS 50 | |
56 | |
57 struct cpu_info { | |
58 long unsigned utime, ntime, stime, itime; | |
59 long unsigned iowtime, irqtime, sirqtime, steal; | |
60 unsigned long long total; | |
61 }; | |
62 | |
63 enum CODE{ | |
64 KEY_UP = 0x100, KEY_DOWN, KEY_HOME, | |
65 KEY_END, KEY_PAGEUP, KEY_PAGEDN, | |
66 }; | |
67 | |
68 struct keycode_map_s { | |
69 char *key; | |
70 int code; | |
71 }; | |
72 | |
73 struct proc_info { | |
74 struct proc_info *next; | |
75 pid_t pid, ppid; | |
76 uid_t uid; | |
77 char name[PROC_NAME_LEN]; | |
78 char tname[PROC_NAME_LEN]; | |
79 char state[4]; | |
80 int prs; | |
81 unsigned long utime, stime, delta_utime, delta_stime, delta_time; | |
82 unsigned long vss, vssrw, rss, rss_shr, drt, drt_shr, stack; | |
83 }; | |
84 | |
85 static struct proc_info *free_procs, **old_procs, **new_procs; | |
86 static struct cpu_info old_cpu[10], new_cpu[10]; //1 total, 8 cores, 1 null | |
87 static int (*proc_cmp)(const void *a, const void *b); | |
88 | |
89 static struct proc_info *find_old_proc(pid_t pid) | |
90 { | |
91 int i; | |
92 | |
93 for (i = 0; old_procs && old_procs[i]; i++) | |
94 if (old_procs[i]->pid == pid) return old_procs[i]; | |
95 | |
96 return NULL; | |
97 } | |
98 | |
99 static void read_stat(char *filename, struct proc_info *proc) | |
100 { | |
101 int nice; | |
102 FILE *file; | |
103 char *open_paren, *close_paren; | |
104 | |
105 if (!(file = fopen(filename, "r"))) return; | |
106 fgets(toybuf, sizeof(toybuf), file); | |
107 fclose(file); | |
108 | |
109 // Split at first '(' and last ')' to get process name. | |
110 open_paren = strchr(toybuf, '('); | |
111 close_paren = strrchr(toybuf, ')'); | |
112 if (!open_paren || !close_paren) return; | |
113 | |
114 *open_paren = *close_paren = '\0'; | |
115 snprintf(proc->tname, PROC_NAME_LEN, "[%s]",open_paren + 1); | |
116 | |
117 // Scan rest of string. | |
118 sscanf(close_paren + 1, " %c %d %*d %*d %*d %*d %*d %*d %*d %*d %*d " | |
119 "%lu %lu %*d %*d %*d %d %*d %*d %*d %lu %ld " | |
120 "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d", | |
121 &proc->state[0], &proc->ppid, &proc->utime, &proc->stime, &nice, | |
122 &proc->vss, &proc->rss, &proc->prs); | |
123 if (!proc->vss && proc->state[0] != 'Z') proc->state[1] = 'W'; | |
124 else proc->state[1] = ' '; | |
125 if (nice < 0 ) proc->state[2] = '<'; | |
126 else if (nice) proc->state[2] = 'N'; | |
127 else proc->state[2] = ' '; | |
128 } | |
129 | |
130 static void read_status(char *filename, struct proc_info *proc) | |
131 { | |
132 FILE *file; | |
133 | |
134 if (!(file = fopen(filename, "r"))) return; | |
135 while (fgets(toybuf, sizeof(toybuf), file)) | |
136 if (sscanf(toybuf, "Uid: %u", &(proc->uid)) == 1) break; | |
137 | |
138 fclose(file); | |
139 } | |
140 | |
141 static void read_cmdline(char *filename, struct proc_info *proc) | |
142 { | |
143 int fd, len, rbytes = 0; | |
144 char *ch, *base, tname[PROC_NAME_LEN]; | |
145 | |
146 if ((fd = open(filename, O_RDONLY)) == -1) return; | |
147 rbytes = readall(fd, toybuf, sizeof(toybuf)); | |
148 close(fd); | |
149 if (rbytes <= 0) { | |
150 strcpy(proc->name, proc->tname); | |
151 return; | |
152 } | |
153 toybuf[rbytes] = '\0'; | |
154 while (--rbytes >= 0 && toybuf[rbytes] == '\0') continue; | |
155 | |
156 snprintf(tname, PROC_NAME_LEN, "%s", proc->tname+1); | |
157 tname[strlen(tname) - 1] = '\0'; | |
158 ch = strchr(toybuf, ' '); | |
159 if (ch) *ch = '\0'; | |
160 base = strrchr(toybuf, '/'); | |
161 if (base) base++; | |
162 else base = toybuf; | |
163 | |
164 for (; rbytes >= 0; rbytes--) | |
165 if ((unsigned char)toybuf[rbytes] < ' ') toybuf[rbytes] = ' '; | |
166 | |
167 if (*base == '-') base++; | |
168 len = strlen(tname); | |
169 if (strncmp(base, tname, len)) { | |
170 len +=3; //{,}, \0 | |
171 rbytes = strlen(toybuf); | |
172 memmove(toybuf+ len, toybuf, rbytes+1); | |
173 snprintf(toybuf, sizeof(toybuf), "{%s}", tname); | |
174 toybuf[len-1] = ' '; | |
175 } | |
176 snprintf(proc->name, PROC_NAME_LEN, "%s", toybuf); | |
177 } | |
178 | |
179 static void add_proc(int proc_num, struct proc_info *proc) | |
180 { | |
181 int i; | |
182 | |
183 if (proc_num >= TT.num_new_procs-1) { | |
184 new_procs = xrealloc(new_procs, (INIT_PROCS + TT.num_new_procs) | |
185 * sizeof(struct proc_info *)); | |
186 for (i = TT.num_new_procs; i < (INIT_PROCS + TT.num_new_procs); i++) | |
187 new_procs[i] = NULL; | |
188 TT.num_new_procs += INIT_PROCS; | |
189 } | |
190 new_procs[proc_num] = proc; | |
191 } | |
192 | |
193 void signal_handler(int sig) | |
194 { | |
195 tcsetattr(STDIN_FILENO, TCSANOW, &TT.inf); | |
196 xputc('\n'); | |
197 signal(sig, SIG_DFL); | |
198 raise(sig); | |
199 _exit(sig | 128); | |
200 } | |
201 | |
202 static int get_key_code(char *ch, int i) | |
203 { | |
204 static struct keycode_map_s type2[] = { | |
205 {"OA",KEY_UP}, {"OB",KEY_DOWN}, {"OH",KEY_HOME}, | |
206 {"OF",KEY_END}, {"[A",KEY_UP}, {"[B",KEY_DOWN}, | |
207 {"[H",KEY_HOME}, {"[F",KEY_END}, {NULL, 0} | |
208 }; | |
209 | |
210 static struct keycode_map_s type3[] = { | |
211 {"[1~", KEY_HOME}, {"[4~", KEY_END}, {"[5~", KEY_PAGEUP}, | |
212 {"[6~", KEY_PAGEDN}, {"[7~", KEY_HOME}, {"[8~", KEY_END}, | |
213 {NULL, 0} | |
214 }; | |
215 struct keycode_map_s *table, *keytable[3] = {type2, type3, NULL}; | |
216 int j; | |
217 | |
218 if ( i > 3 || i < 1) return -1; | |
219 | |
220 for (j=0; (table = keytable[j]); j++) { | |
221 while (table->key) { | |
222 if (!strncmp(ch, table->key, i)) break; | |
223 table++; | |
224 } | |
225 if (table->key) { | |
226 if (i == 1 || (i == 2 && j)) return 1; | |
227 return table->code; | |
228 } | |
229 } | |
230 return -1; | |
231 } | |
232 | |
233 static int read_input(int delay) | |
234 { | |
235 struct pollfd pfd[1]; | |
236 int ret, fret = 0, cnt = 0, escproc = 0, timeout = delay * 1000; | |
237 char ch, seq[4] = {0,}; | |
238 struct termios newf; | |
239 | |
240 tcgetattr(0, &TT.inf); | |
241 if (toys.optflags & FLAG_b) { | |
242 sleep(delay); | |
243 return 0; | |
244 } | |
245 pfd[0].fd = 0; | |
246 pfd[0].events = POLLIN; | |
247 | |
248 //prepare terminal for input, without Enter of Carriage return | |
249 memcpy(&newf, &TT.inf, sizeof(struct termios)); | |
250 newf.c_lflag &= ~(ICANON | ECHO | ECHONL); | |
251 newf.c_cc[VMIN] = 1; | |
252 newf.c_cc[VTIME] = 0; | |
253 tcsetattr(0, TCSANOW, &newf); | |
254 | |
255 while (1) { | |
256 if ((ret = poll(pfd, 1, timeout)) >= 0) break; | |
257 else { | |
258 if (timeout > 0) timeout--; | |
259 if (errno == EINTR) continue; | |
260 perror_exit("poll"); | |
261 } | |
262 } | |
263 | |
264 while (ret) { | |
265 if (read(STDIN_FILENO, &ch, 1) != 1) toys.optflags |= FLAG_b; | |
266 else if (ch == '\033' || escproc) { | |
267 int code; | |
268 //process ESC keys | |
269 if (!escproc) { | |
270 if (!poll(pfd, 1, 50)) break; //no more chars | |
271 escproc = 1; | |
272 continue; | |
273 } | |
274 seq[cnt++] = ch; | |
275 code = get_key_code(seq, cnt); | |
276 switch(code) { | |
277 case -1: //no match | |
278 fret = 0; | |
279 break; | |
280 case 1: //read more | |
281 continue; | |
282 default: // got the key | |
283 fret = code; | |
284 break; | |
285 } | |
286 } else if ((ch == TT.inf.c_cc[VINTR]) | |
287 || (ch == TT.inf.c_cc[VEOF])) | |
288 fret = 'q'; | |
289 else fret = ch | 0x20; | |
290 break; | |
291 } | |
292 tcsetattr(0, TCSANOW, &TT.inf); | |
293 return fret; | |
294 } | |
295 | |
296 // Allocation for Processes | |
297 static struct proc_info *alloc_proc(void) | |
298 { | |
299 struct proc_info *proc; | |
300 | |
301 if (free_procs) { | |
302 proc = free_procs; | |
303 free_procs = free_procs->next; | |
304 memset(proc, 0, sizeof(*proc)); | |
305 } else proc = xzalloc(sizeof(*proc)); | |
306 | |
307 return proc; | |
308 } | |
309 | |
310 static void free_proc_list(struct proc_info *procs) | |
311 { | |
312 struct proc_info *tmp = procs; | |
313 | |
314 for (;tmp; tmp = procs) { | |
315 procs = procs->next; | |
316 free(tmp); | |
317 } | |
318 } | |
319 | |
320 // Free allocated Processes in order to avoid memory leaks | |
321 static void free_proc(struct proc_info *proc) | |
322 { | |
323 proc->next = free_procs; | |
324 free_procs = proc; | |
325 } | |
326 | |
327 static struct proc_info *add_new_proc(pid_t pid, pid_t tid) | |
328 { | |
329 char filename[64]; | |
330 struct proc_info *proc = alloc_proc(); | |
331 | |
332 proc->pid = (tid)? tid : pid; | |
333 if (!tid) { | |
334 sprintf(filename, "/proc/%d/stat", pid); | |
335 read_stat(filename, proc); | |
336 sprintf(filename, "/proc/%d/cmdline", pid); | |
337 read_cmdline(filename, proc); | |
338 sprintf(filename, "/proc/%d/status", pid); | |
339 read_status(filename, proc); | |
340 } else{ | |
341 sprintf(filename, "/proc/%d/task/%d/stat", pid,tid); | |
342 read_stat(filename, proc); | |
343 sprintf(filename, "/proc/%d/task/%d/cmdline", pid, tid); | |
344 read_cmdline(filename, proc); | |
345 } | |
346 return proc; | |
347 } | |
348 | |
349 static void read_smaps(pid_t pid, struct proc_info *p) | |
350 { | |
351 FILE *fp; | |
352 char *line; | |
353 size_t len; | |
354 long long start, end, val, prvcl, prvdr, shrdr, shrcl; | |
355 int count; | |
356 | |
357 p->vss = p->rss = 0; | |
358 start = end = val = prvcl = prvdr = shrdr = shrcl = 0; | |
359 sprintf(toybuf, "/proc/%u/smaps", pid); | |
360 if (!(fp = fopen(toybuf, "r"))) { | |
361 error_msg("No %ld\n", (long)pid); | |
362 return; | |
363 } | |
364 for (;;) { | |
365 int off; | |
366 | |
367 line = 0; | |
368 if (0 >= getline(&line, &len, fp)) break; | |
369 count = sscanf(line, "%llx-%llx %s %*s %*s %*s %n", | |
370 &start, &end, toybuf, &off); | |
371 | |
372 if (count == 3) { | |
373 end = end - start; | |
374 if (strncmp(line+off, "/dev/", 5) || !strcmp(line+off, "/dev/zero\n")) { | |
375 p->vss += end; | |
376 if (toybuf[1] == 'w') p->vssrw += end; | |
377 } | |
378 if (line[off] && !strncmp(line+off, "[stack]",7)) p->stack += end; | |
379 } else { | |
380 if (0<sscanf(line, "Private_Clean: %lld", &val)) prvcl += val; | |
381 if (0<sscanf(line, "Private_Dirty: %lld", &val)) prvdr += val; | |
382 if (0<sscanf(line, "Shared_Dirty: %lld", &val)) shrdr += val; | |
383 if (0<sscanf(line, "Shared_Clean: %lld", &val)) shrcl += val; | |
384 } | |
385 free(line); | |
386 } | |
387 free(line); //incase it broke out. | |
388 p->rss_shr = shrdr + shrcl; | |
389 p->drt = prvdr + shrdr; | |
390 p->drt_shr = shrdr; | |
391 p->rss = p->rss_shr + prvdr + prvcl; | |
392 fclose(fp); | |
393 } | |
394 | |
395 static void read_procs(void) // Read Processes | |
396 { | |
397 DIR *proc_dir, *thr_dir; | |
398 struct dirent *pid_dir, *t_dir; | |
399 struct proc_info *proc; | |
400 pid_t pid, tid; | |
401 int proc_num = 0; | |
402 | |
403 proc_dir = opendir("/proc"); | |
404 if (!proc_dir) perror_exit("Could not open /proc"); | |
405 | |
406 new_procs = xzalloc(INIT_PROCS * sizeof(struct proc_info *)); | |
407 TT.num_new_procs = INIT_PROCS; | |
408 | |
409 while ((pid_dir = readdir(proc_dir))) { | |
410 if (!isdigit(pid_dir->d_name[0])) continue; | |
411 | |
412 pid = atoi(pid_dir->d_name); | |
413 proc = add_new_proc(pid, 0); | |
414 if (TT.m_flag) { | |
415 read_smaps(pid, proc); | |
416 if (!proc->vss) { | |
417 free(proc); | |
418 continue; | |
419 } | |
420 } | |
421 add_proc(proc_num++, proc); | |
422 | |
423 if (TT.threads) { | |
424 char filename[64]; | |
425 uid_t uid = proc->uid; | |
426 | |
427 sprintf(filename,"/proc/%d/task",pid); | |
428 if ((thr_dir = opendir(filename))) { | |
429 while ((t_dir = readdir(thr_dir))) { | |
430 if (!isdigit(t_dir->d_name[0])) continue; | |
431 | |
432 tid = atoi(t_dir->d_name); | |
433 if (pid == tid) continue; | |
434 proc = add_new_proc(pid, tid); | |
435 proc->uid = uid; //child will have same uid as parent. | |
436 add_proc(proc_num++, proc); | |
437 } | |
438 closedir(thr_dir); | |
439 } | |
440 } | |
441 } | |
442 | |
443 closedir(proc_dir); | |
444 TT.num_new_procs = proc_num; | |
445 } | |
446 | |
447 //calculate percentage. | |
448 static char* show_percent(long unsigned num, long unsigned den) | |
449 { | |
450 long res; | |
451 static char ch, buff[12]={'\0'}; | |
452 | |
453 if(num > den) num = den; | |
454 res = (num * 100)/den; | |
455 sprintf(buff,"%ld", (num * 100)% den); | |
456 ch = *buff; | |
457 sprintf(buff, "%ld.%c",res, ch); | |
458 return buff; | |
459 } | |
460 | |
461 static int print_header(struct sysinfo *info, unsigned int cols) | |
462 { | |
463 int fd, j, k, rows =0; | |
464 long unsigned total, meminfo_cached, anon, meminfo_mapped, | |
465 meminfo_slab, meminfo_dirty, meminfo_writeback, swapT, swapF; | |
466 char *buff; | |
467 | |
468 fd = xopen("/proc/meminfo", O_RDONLY); | |
469 while ((buff = get_line(fd))) { | |
470 if (!strncmp(buff, "Cached", 6)) | |
471 sscanf(buff,"%*s %lu\n",&meminfo_cached); | |
472 else if (!strncmp(buff, "AnonPages", 9)) | |
473 sscanf(buff,"%*s %lu\n",&anon); | |
474 else if (!strncmp(buff, "Mapped", 6)) | |
475 sscanf(buff,"%*s %lu\n",&meminfo_mapped); | |
476 else if (!strncmp(buff, "Slab", 4)) | |
477 sscanf(buff,"%*s %lu\n",&meminfo_slab); | |
478 else if (!strncmp(buff, "Dirty", 5)) | |
479 sscanf(buff,"%*s %lu\n",&meminfo_dirty); | |
480 else if (!strncmp(buff, "Writeback", 9)) | |
481 sscanf(buff,"%*s %lu\n",&meminfo_writeback); | |
482 else if (!strncmp(buff, "SwapTotal", 9)) | |
483 sscanf(buff,"%*s %lu\n",&swapT); | |
484 else if (!strncmp(buff, "SwapFree", 8)) | |
485 sscanf(buff,"%*s %lu\n",&swapF); | |
486 free(buff); | |
487 } | |
488 close(fd); | |
489 | |
490 if (!(toys.optflags & FLAG_b)) printf("\033[H\033[J"); | |
491 | |
492 if (TT.m_flag){ | |
493 sprintf(toybuf, "Mem total:%lu anon:%lu map:%lu free:%lu", | |
494 ((info->totalram) >> 10), anon, meminfo_mapped, | |
495 ((info->freeram) >> 10)); | |
496 printf("%.*s\n", cols, toybuf); | |
497 | |
498 sprintf(toybuf, "slab:%lu buf:%lu cache:%lu dirty:%lu write:%lu", | |
499 meminfo_slab, ((info->bufferram) >>10), meminfo_cached, | |
500 meminfo_dirty,meminfo_writeback); | |
501 printf("%.*s\n", cols, toybuf); | |
502 | |
503 sprintf(toybuf, "Swap total:%lu free:%lu",swapT, swapF); | |
504 printf("%.*s\n", cols, toybuf); | |
505 rows += 3; | |
506 } else { | |
507 sprintf(toybuf,"Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached", | |
508 (info->totalram-info->freeram) >>10, (info->freeram) >>10, | |
509 (info->sharedram) >>10, (info->bufferram) >>10, meminfo_cached); | |
510 printf("%.*s\n", cols, toybuf); | |
511 | |
512 for (k = 1; new_cpu[k].total; k++) { | |
513 j = 0; | |
514 if (!TT.smp) { | |
515 k = 0; | |
516 j = sprintf(toybuf,"CPU:"); | |
517 } else j = sprintf(toybuf,"CPU%d:", k-1); | |
518 | |
519 total = (new_cpu[k].total) - (old_cpu[k].total); | |
520 if (!total) total = 1; //avoid denominator as 0, FPE | |
521 j += sprintf(toybuf + j," %s%% usr", | |
522 show_percent((new_cpu[k].utime - old_cpu[k].utime), total)); | |
523 j += sprintf(toybuf+j," %s%% sys", | |
524 show_percent((new_cpu[k].stime - old_cpu[k].stime), total)); | |
525 j += sprintf(toybuf+j," %s%% nic", | |
526 show_percent(new_cpu[k].ntime - old_cpu[k].ntime, total)); | |
527 j += sprintf(toybuf+j," %s%% idle", | |
528 show_percent(new_cpu[k].itime - old_cpu[k].itime, total)); | |
529 j += sprintf(toybuf+j," %s%% io", | |
530 show_percent((new_cpu[k].iowtime - old_cpu[k].iowtime), total)); | |
531 j += sprintf(toybuf+j," %s%% irq", | |
532 show_percent(new_cpu[k].irqtime - old_cpu[k].irqtime, total)); | |
533 j += sprintf(toybuf+j," %s%% sirq", | |
534 show_percent(new_cpu[k].sirqtime - old_cpu[k].sirqtime, total)); | |
535 printf("%.*s\n", cols, toybuf); | |
536 if (!TT.smp) break; | |
537 } | |
538 | |
539 if ((buff = readfile("/proc/loadavg", NULL, 0))) { | |
540 buff[strlen(buff) -1] = '\0'; //removing '\n' at end | |
541 sprintf(toybuf, "Load average: %s", buff); | |
542 printf("%.*s\n", cols, toybuf); | |
543 free(buff); | |
544 } | |
545 rows += 2 + ((TT.smp) ? k-1 : 1); | |
546 } | |
547 return rows; | |
548 } | |
549 | |
550 static void print_procs(void) | |
551 { | |
552 int i, j = 0; | |
553 struct proc_info *old_proc, *proc; | |
554 long unsigned total_delta_time; | |
555 struct passwd *user; | |
556 char *user_str, user_buf[20]; | |
557 struct sysinfo info; | |
558 unsigned int cols=0, rows =0; | |
559 | |
560 terminal_size(&cols, &rows); | |
561 if (!rows){ | |
562 rows = 24; //on serial consoles setting default | |
563 cols = 79; | |
564 } | |
565 if (toys.optflags & FLAG_b) rows = INT_MAX; | |
566 TT.rows = rows; | |
567 | |
568 for (i = 0; i < TT.num_new_procs; i++) { | |
569 if (new_procs[i]) { | |
570 old_proc = find_old_proc(new_procs[i]->pid); | |
571 if (old_proc) { | |
572 new_procs[i]->delta_utime = new_procs[i]->utime - old_proc->utime; | |
573 new_procs[i]->delta_stime = new_procs[i]->stime - old_proc->stime; | |
574 } else { | |
575 new_procs[i]->delta_utime = 0; | |
576 new_procs[i]->delta_stime = 0; | |
577 } | |
578 new_procs[i]->delta_time = new_procs[i]->delta_utime | |
579 + new_procs[i]->delta_stime; | |
580 } | |
581 } | |
582 | |
583 total_delta_time = new_cpu[0].total - old_cpu[0].total; | |
584 if (!total_delta_time) total_delta_time = 1; | |
585 | |
586 qsort(new_procs, TT.num_new_procs, sizeof(struct proc_info *), proc_cmp); | |
587 | |
588 //Memory details | |
589 sysinfo(&info); | |
590 info.totalram *= info.mem_unit; | |
591 info.freeram *= info.mem_unit; | |
592 info.sharedram *= info.mem_unit; | |
593 info.bufferram *= info.mem_unit; | |
594 | |
595 rows -= print_header(&info, cols); | |
596 | |
597 if (TT.m_flag) { | |
598 sprintf(toybuf, "%5s %5s %5s %5s %5s %5s %5s %5s %s", "PID", "VSZ", "VSZRW", | |
599 "RSS", "(SHR)", "DIRTY", "(SHR)", "STACK", "COMMAND"); | |
600 toybuf[11 + TT.cmp_field*6] = (TT.reverse)?'_':'^'; //11 for PID,VSZ fields | |
601 } else sprintf(toybuf, "%5s %5s %-8s %4s %5s %5s %4s %5s %s", "PID", "PPID", | |
602 "USER", "STAT", "VSZ", "%VSZ", "CPU" , "%CPU", "COMMAND"); | |
603 | |
604 printf((toys.optflags & FLAG_b)?"%.*s\n":"\033[7m%.*s\033[0m\n",cols, toybuf); | |
605 rows--; | |
606 for (i = TT.scroll_offset; i < TT.num_new_procs; i++) { | |
607 j = 0; | |
608 proc = new_procs[i]; | |
609 | |
610 user = getpwuid(proc->uid); | |
611 if (user && user->pw_name) { | |
612 user_str = user->pw_name; | |
613 } else { | |
614 snprintf(user_buf, 20, "%d", proc->uid); | |
615 user_str = user_buf; | |
616 } | |
617 | |
618 if (!TT.m_flag ) | |
619 { | |
620 float vss_percentage = (float)(proc->vss)/info.totalram * 100; | |
621 | |
622 j = sprintf(toybuf, "%5d %5d %-8.8s %-4s",proc->pid, proc->ppid, user_str, | |
623 proc->state); | |
624 | |
625 if ((proc->vss >> 10) >= 100000) | |
626 j += sprintf(toybuf + j, " %4lum", ((proc->vss >> 10) >> 10)); | |
627 else j += sprintf(toybuf+j, " %5lu", (proc->vss >> 10)); | |
628 | |
629 sprintf(toybuf + j," %5.1f %4d %5s %s", vss_percentage, proc->prs, | |
630 show_percent(proc->delta_time, total_delta_time), | |
631 ((proc->name[0])? proc->name : proc->tname)); | |
632 printf("%.*s", cols, toybuf); | |
633 } else { | |
634 j = sprintf(toybuf, "%5d",proc->pid); | |
635 | |
636 if ((proc->vss >> 10) >= 100000) | |
637 j += sprintf(toybuf + j, " %4lum", ((proc->vss >> 10) >> 10)); | |
638 else j += sprintf(toybuf+j, " %5lu", (proc->vss >> 10)); | |
639 if ((proc->vssrw >>10) >= 100000) | |
640 j += sprintf(toybuf + j, " %4lum", ((proc->vssrw >> 10) >> 10)); | |
641 else j += sprintf(toybuf+j, " %5lu", (proc->vssrw >> 10)); | |
642 if (proc->rss >= 100000) | |
643 j += sprintf(toybuf + j, " %4lum", ((proc->rss >> 10))); | |
644 else j += sprintf(toybuf+j, " %5lu", proc->rss); | |
645 if (proc->rss_shr >= 100000) | |
646 j += sprintf(toybuf + j, " %4lum", (proc->rss_shr >> 10)); | |
647 else j += sprintf(toybuf+j, " %5lu", proc->rss_shr); | |
648 if (proc->drt >= 100000) | |
649 j += sprintf(toybuf + j, " %4lum", (proc->drt >> 10)); | |
650 else j += sprintf(toybuf+j, " %5lu", proc->drt); | |
651 if (proc->drt_shr >= 100000) | |
652 j += sprintf(toybuf + j, " %4lum", (proc->drt_shr >> 10)); | |
653 else j += sprintf(toybuf+j, " %5lu", proc->drt_shr); | |
654 if ((proc->stack >>10) >= 100000) | |
655 j += sprintf(toybuf + j, " %4lum", ((proc->stack >> 10) >> 10)); | |
656 else j += sprintf(toybuf+j, " %5lu", (proc->stack >> 10)); | |
657 | |
658 sprintf(toybuf + j," %s",((proc->name[0])? proc->name : proc->tname)); | |
659 printf("%.*s", cols, toybuf); | |
660 } | |
661 rows--; | |
662 if (!rows) { | |
663 xputc('\r'); | |
664 break; //don't print any more process details. | |
665 } else xputc('\n'); | |
666 } | |
667 } | |
668 | |
669 /* | |
670 * Free old processes(displayed in old iteration) in order to | |
671 * avoid memory leaks | |
672 */ | |
673 static void free_procs_arr(struct proc_info **procs) | |
674 { | |
675 int i; | |
676 for (i = 0; procs && procs[i]; i++) | |
677 free_proc(procs[i]); | |
678 | |
679 free(procs); | |
680 } | |
681 | |
682 static int numcmp(long long a, long long b) | |
683 { | |
684 if (a < b) return (TT.reverse)?-1 : 1; | |
685 if (a > b) return (TT.reverse)?1 : -1; | |
686 return 0; | |
687 } | |
688 | |
689 static int top_mem_cmp(const void *a, const void *b) | |
690 { | |
691 char *pa, *pb; | |
692 | |
693 int n = offsetof(struct proc_info, vss) + TT.cmp_field * sizeof(unsigned long); | |
694 pa = *((char **)a); pb = *((char **)b); | |
695 return numcmp(*(unsigned long*)(pa+n), *(unsigned long*)(pb+n)); | |
696 } | |
697 | |
698 static int proc_time_cmp(const void *a, const void *b) | |
699 { | |
700 struct proc_info *pa, *pb; | |
701 | |
702 pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); | |
703 return numcmp(pa->utime + pa->stime, pb->utime+pa->stime); | |
704 } | |
705 | |
706 /* | |
707 * Function to compare CPU usgae % while displaying processes | |
708 * according to CPU usage | |
709 */ | |
710 static int proc_cpu_cmp(const void *a, const void *b) | |
711 { | |
712 struct proc_info *pa, *pb; | |
713 | |
714 pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); | |
715 return numcmp(pa->delta_time, pb->delta_time); | |
716 } | |
717 | |
718 /* | |
719 * Function to compare memory taking by a process at the time of | |
720 * displaying processes according to Memory usage | |
721 */ | |
722 static int proc_vss_cmp(const void *a, const void *b) | |
723 { | |
724 struct proc_info *pa, *pb; | |
725 | |
726 pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); | |
727 return numcmp(pa->vss, pb->vss); | |
728 } | |
729 | |
730 static int proc_pid_cmp(const void *a, const void *b) | |
731 { | |
732 struct proc_info *pa, *pb; | |
733 | |
734 pa = *((struct proc_info **)a); pb = *((struct proc_info **)b); | |
735 return numcmp(pa->pid, pb->pid); | |
736 } | |
737 | |
738 /* Read CPU stats for all the cores, assuming max 8 cores | |
739 * to be present here. | |
740 */ | |
741 static void read_cpu_stat() | |
742 { | |
743 int i; | |
744 size_t len; | |
745 char *line = 0, *params = "%lu %lu %lu %lu %lu %lu %lu %lu"; | |
746 FILE *fp = xfopen("/proc/stat", "r"); | |
747 | |
748 for (i = 0; i<=8 && getline(&line, &len, fp) > 0; i++) { | |
749 if (i) sprintf(toybuf, "cpu%d %s", i-1, params); | |
750 else sprintf(toybuf, "cpu %s", params); | |
751 len = sscanf(line, toybuf, &new_cpu[i].utime, &new_cpu[i].ntime, | |
752 &new_cpu[i].stime, &new_cpu[i].itime, &new_cpu[i].iowtime, | |
753 &new_cpu[i].irqtime, &new_cpu[i].sirqtime, &new_cpu[i].steal); | |
754 if (len == 8) | |
755 new_cpu[i].total = new_cpu[i].utime + new_cpu[i].ntime + new_cpu[i].stime | |
756 + new_cpu[i].itime + new_cpu[i].iowtime + new_cpu[i].irqtime | |
757 + new_cpu[i].sirqtime + new_cpu[i].steal; | |
758 | |
759 free(line); | |
760 line = 0; | |
761 } | |
762 fclose(fp); | |
763 } | |
764 | |
765 void top_main(void ) | |
766 { | |
767 int get_key; | |
768 | |
769 proc_cmp = &proc_cpu_cmp; | |
770 if ( TT.delay < 0) TT.delay = 3; | |
771 if (toys.optflags & FLAG_m) { | |
772 proc_cmp = &top_mem_cmp; | |
773 TT.m_flag = 1; | |
774 } | |
775 | |
776 sigatexit(signal_handler); | |
777 read_cpu_stat(); | |
778 get_key = read_input(0); | |
779 | |
780 while (!(toys.optflags & FLAG_n) || TT.iterations--) { | |
781 old_procs = new_procs; | |
782 memcpy(old_cpu, new_cpu, sizeof(old_cpu)); | |
783 read_procs(); | |
784 read_cpu_stat(); | |
785 print_procs(); | |
786 free_procs_arr(old_procs); | |
787 if ((toys.optflags & FLAG_n) && !TT.iterations) break; | |
788 | |
789 get_key = read_input(TT.delay); | |
790 if (get_key == 'q') break; | |
791 | |
792 switch(get_key) { | |
793 case 'n': | |
794 proc_cmp = &proc_pid_cmp; | |
795 TT.m_flag = 0; | |
796 break; | |
797 case 'h': | |
798 if (!TT.m_flag) TT.threads ^= 1; | |
799 break; | |
800 case 'm': | |
801 proc_cmp = &proc_vss_cmp; | |
802 TT.m_flag = 0; | |
803 break; | |
804 case 'r': | |
805 TT.reverse ^= 1; | |
806 break; | |
807 case 'c': | |
808 case '1': | |
809 TT.smp ^= 1; | |
810 break; | |
811 case 's': | |
812 TT.m_flag = 1; | |
813 TT.cmp_field = (TT.cmp_field + 1) % 7;//7 sort fields, vss,vssrw... | |
814 proc_cmp = &top_mem_cmp; | |
815 break; | |
816 case 'p': | |
817 proc_cmp = &proc_cpu_cmp; | |
818 TT.m_flag = 0; | |
819 break; | |
820 case 't': | |
821 proc_cmp = &proc_time_cmp; | |
822 TT.m_flag = 0; | |
823 break; | |
824 case KEY_UP: | |
825 TT.scroll_offset--; | |
826 break; | |
827 case KEY_DOWN: | |
828 TT.scroll_offset++; | |
829 break; | |
830 case KEY_HOME: | |
831 TT.scroll_offset = 0; | |
832 break; | |
833 case KEY_END: | |
834 TT.scroll_offset = TT.num_new_procs - TT.rows/2; | |
835 break; | |
836 case KEY_PAGEUP: | |
837 TT.scroll_offset -= TT.rows/2; | |
838 break; | |
839 case KEY_PAGEDN: | |
840 TT.scroll_offset += TT.rows/2; | |
841 break; | |
842 } | |
843 if (TT.scroll_offset >= TT.num_new_procs) TT.scroll_offset = TT.num_new_procs-1; | |
844 if (TT.scroll_offset < 0) TT.scroll_offset = 0; | |
845 } | |
846 xputc('\n'); | |
847 if (CFG_TOYBOX_FREE) { | |
848 free_proc_list(free_procs); | |
849 free_procs = NULL; | |
850 free_procs_arr(new_procs); | |
851 free_proc_list(free_procs); | |
852 } | |
853 } |