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