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 
16     usage: top [-mb] [ -d seconds ] [ -n iterations ]
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 
find_old_proc(pid_t pid)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 
read_stat(char * filename,struct proc_info * proc)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 
read_status(char * filename,struct proc_info * proc)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 
read_cmdline(char * filename,struct proc_info * proc)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 
add_proc(int proc_num,struct proc_info * proc)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 
signal_handler(int sig)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 
get_key_code(char * ch,int i)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 
read_input(int delay)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
alloc_proc(void)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 
free_proc_list(struct proc_info * procs)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
free_proc(struct proc_info * proc)321 static void free_proc(struct proc_info *proc)
322 {
323   proc->next = free_procs;
324   free_procs = proc;
325 }
326 
add_new_proc(pid_t pid,pid_t tid)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 
read_smaps(pid_t pid,struct proc_info * p)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 
read_procs(void)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.
show_percent(long unsigned num,long unsigned den)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 
print_header(struct sysinfo * info,unsigned int cols)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 
print_procs(void)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  */
free_procs_arr(struct proc_info ** procs)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 
numcmp(long long a,long long b)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 
top_mem_cmp(const void * a,const void * b)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 
proc_time_cmp(const void * a,const void * b)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  */
proc_cpu_cmp(const void * a,const void * b)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  */
proc_vss_cmp(const void * a,const void * b)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 
proc_pid_cmp(const void * a,const void * b)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  */
read_cpu_stat()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 
top_main(void)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 }
854