OSDN Git Service

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