Home | History | Annotate | Download | only in pending
      1 /* top.c - Provide a view of process activity in real time.
      2  *
      3  * Copyright 2013 Bilal Qureshi <bilal.jmi (at) gmail.com>
      4  * Copyright 2013 Ashwini Kumar <ak.ashwini (at) gmail.com>
      5  * Copyright 2013 Kyungwan Han <asura321 (at) 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 
     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 }
    854